1 namespace System.Web.UI.WebControls { 2 using System.Web.UI.WebControls.Expressions; 3 using System; 4 using System.Collections; 5 using System.Collections.Generic; 6 using System.Collections.Specialized; 7 using System.Globalization; 8 using System.Linq; 9 using System.Security.Permissions; 10 using System.Web; 11 using System.Web.Resources; 12 using System.Web.UI; 13 using System.Web.UI.WebControls; 14 using System.Diagnostics.CodeAnalysis; 15 using System.ComponentModel; 16 17 18 public abstract class QueryableDataSourceView : DataSourceView, IStateManager { 19 //basic query parameters for any Queryable source 20 private ParameterCollection _whereParameters; 21 private ParameterCollection _orderByParameters; 22 private ParameterCollection _orderGroupsByParameters; 23 private ParameterCollection _selectNewParameters; 24 private ParameterCollection _groupByParameters; 25 26 // CUD operations 27 private ParameterCollection _deleteParameters; 28 private ParameterCollection _updateParameters; 29 private ParameterCollection _insertParameters; 30 31 private HttpContext _context; 32 private DataSourceControl _owner; 33 private IDynamicQueryable _queryable; 34 35 private string _groupBy; 36 private string _orderBy; 37 private string _orderGroupsBy; 38 private string _selectNew; 39 private string _where; 40 41 private bool _autoGenerateOrderByClause; 42 private bool _autoGenerateWhereClause; 43 private bool _autoPage = true; 44 private bool _autoSort = true; 45 private bool _isTracking; 46 47 protected static readonly object EventSelected = new object(); 48 protected static readonly object EventSelecting = new object(); 49 private static readonly object EventQueryCreated = new object(); 50 51 // using Hashtable for original values so that ObjectStateFormatter will serialize it properly in ViewState. 52 private Hashtable _originalValues; 53 54 QueryableDataSourceView(DataSourceControl owner, string viewName, HttpContext context)55 protected QueryableDataSourceView(DataSourceControl owner, string viewName, HttpContext context) 56 : this(owner, viewName, context, new DynamicQueryableWrapper()) { 57 _context = context; 58 _owner = owner; 59 } 60 QueryableDataSourceView(DataSourceControl owner, string viewName, HttpContext context, IDynamicQueryable queryable)61 internal QueryableDataSourceView(DataSourceControl owner, string viewName, HttpContext context, IDynamicQueryable queryable) 62 : base(owner, viewName) { 63 _context = context; 64 _queryable = queryable; 65 _owner = owner; 66 } 67 68 public bool AutoGenerateOrderByClause { 69 get { 70 return _autoGenerateOrderByClause; 71 } 72 set { 73 if (_autoGenerateOrderByClause != value) { 74 _autoGenerateOrderByClause = value; 75 OnDataSourceViewChanged(EventArgs.Empty); 76 } 77 } 78 } 79 80 public bool AutoGenerateWhereClause { 81 get { 82 return _autoGenerateWhereClause; 83 } 84 set { 85 if (_autoGenerateWhereClause != value) { 86 _autoGenerateWhereClause = value; 87 OnDataSourceViewChanged(EventArgs.Empty); 88 } 89 } 90 } 91 92 public virtual bool AutoPage { 93 get { 94 return _autoPage; 95 } 96 set { 97 if (_autoPage != value) { 98 _autoPage = value; 99 OnDataSourceViewChanged(EventArgs.Empty); 100 } 101 } 102 } 103 104 public virtual bool AutoSort { 105 get { 106 return _autoSort; 107 } 108 set { 109 if (_autoSort != value) { 110 _autoSort = value; 111 OnDataSourceViewChanged(EventArgs.Empty); 112 } 113 } 114 } 115 116 public override bool CanDelete { 117 get { 118 return false; 119 } 120 } 121 122 public override bool CanInsert { 123 get { 124 return false; 125 } 126 } 127 128 // When AutoPage is false the user should manually page in the Selecting event. 129 public override bool CanPage { 130 get { 131 return true; 132 } 133 } 134 135 // When AutoPage is false the user must set the total row count in the Selecting event. 136 public override bool CanRetrieveTotalRowCount { 137 get { 138 return true; 139 } 140 } 141 142 // When AutoSort is false the user should manually sort in the Selecting event. 143 public override bool CanSort { 144 get { 145 return true; 146 } 147 } 148 149 public override bool CanUpdate { 150 get { 151 return false; 152 } 153 } 154 155 public virtual ParameterCollection DeleteParameters { 156 get { 157 if (_deleteParameters == null) { 158 _deleteParameters = new ParameterCollection(); 159 } 160 return _deleteParameters; 161 } 162 } 163 164 protected abstract Type EntityType { 165 get; 166 } 167 168 public virtual ParameterCollection GroupByParameters { 169 get { 170 if (_groupByParameters == null) { 171 _groupByParameters = new ParameterCollection(); 172 _groupByParameters.ParametersChanged += new EventHandler(OnQueryParametersChanged); 173 if (_isTracking) { 174 DataSourceHelper.TrackViewState(_groupByParameters); 175 } 176 } 177 return _groupByParameters; 178 } 179 } 180 181 protected bool IsTrackingViewState { 182 get { 183 return _isTracking; 184 } 185 } 186 187 public virtual ParameterCollection InsertParameters { 188 get { 189 if (_insertParameters == null) { 190 _insertParameters = new ParameterCollection(); 191 } 192 return _insertParameters; 193 } 194 } 195 196 public virtual ParameterCollection OrderByParameters { 197 get { 198 if (_orderByParameters == null) { 199 _orderByParameters = new ParameterCollection(); 200 _orderByParameters.ParametersChanged += new EventHandler(OnQueryParametersChanged); 201 if (_isTracking) { 202 DataSourceHelper.TrackViewState(_orderByParameters); 203 } 204 } 205 return _orderByParameters; 206 } 207 } 208 209 public virtual ParameterCollection OrderGroupsByParameters { 210 get { 211 if (_orderGroupsByParameters == null) { 212 _orderGroupsByParameters = new ParameterCollection(); 213 _orderGroupsByParameters.ParametersChanged += new EventHandler(OnQueryParametersChanged); 214 if (_isTracking) { 215 DataSourceHelper.TrackViewState(_orderGroupsByParameters); 216 } 217 } 218 return _orderGroupsByParameters; 219 } 220 } 221 222 223 public virtual string OrderBy { 224 get { 225 return _orderBy ?? String.Empty; 226 } 227 set { 228 if (_orderBy != value) { 229 _orderBy = value; 230 OnDataSourceViewChanged(EventArgs.Empty); 231 } 232 } 233 } 234 235 236 public virtual string OrderGroupsBy { 237 get { 238 return _orderGroupsBy ?? String.Empty; 239 } 240 set { 241 if (_orderGroupsBy != value) { 242 _orderGroupsBy = value; 243 OnDataSourceViewChanged(EventArgs.Empty); 244 } 245 } 246 } 247 248 public virtual string GroupBy { 249 get { 250 return _groupBy ?? String.Empty; 251 } 252 set { 253 if (_groupBy != value) { 254 _groupBy = value; 255 OnDataSourceViewChanged(EventArgs.Empty); 256 } 257 } 258 } 259 260 public virtual ParameterCollection SelectNewParameters { 261 get { 262 if (_selectNewParameters == null) { 263 _selectNewParameters = new ParameterCollection(); 264 _selectNewParameters.ParametersChanged += new EventHandler(OnQueryParametersChanged); 265 if (_isTracking) { 266 DataSourceHelper.TrackViewState(_selectNewParameters); 267 } 268 } 269 return _selectNewParameters; 270 } 271 } 272 273 [SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix", 274 Justification = "SelectNew refers to a projection and 'New' is not used as a suffix in this case")] 275 public virtual string SelectNew { 276 get { 277 return _selectNew ?? String.Empty; 278 } 279 set { 280 if (_selectNew != value) { 281 _selectNew = value; 282 OnDataSourceViewChanged(EventArgs.Empty); 283 } 284 } 285 } 286 287 public virtual ParameterCollection WhereParameters { 288 get { 289 if (_whereParameters == null) { 290 _whereParameters = new ParameterCollection(); 291 _whereParameters.ParametersChanged += new EventHandler(OnQueryParametersChanged); 292 if (_isTracking) { 293 DataSourceHelper.TrackViewState(_whereParameters); 294 } 295 } 296 return _whereParameters; 297 } 298 } 299 300 public virtual string Where { 301 get { 302 return _where ?? String.Empty; 303 } 304 set { 305 if (_where != value) { 306 _where = value; 307 OnDataSourceViewChanged(EventArgs.Empty); 308 } 309 } 310 } 311 312 313 public virtual ParameterCollection UpdateParameters { 314 get { 315 if (_updateParameters == null) { 316 _updateParameters = new ParameterCollection(); 317 } 318 return _updateParameters; 319 } 320 } 321 322 323 [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", Justification = "Derived classes will use this to as the ParametersChanged EventHandler")] OnQueryParametersChanged(object sender, EventArgs e)324 protected void OnQueryParametersChanged(object sender, EventArgs e) { 325 RaiseViewChanged(); 326 } 327 328 [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "An event exists already and it it protected")] RaiseViewChanged()329 public void RaiseViewChanged() { 330 OnDataSourceViewChanged(EventArgs.Empty); 331 } 332 333 /// <summary> 334 /// Gets the result to apply query 335 /// </summary> 336 /// <returns></returns> GetSource(QueryContext context)337 protected abstract object GetSource(QueryContext context); 338 CreateQueryContext(DataSourceSelectArguments arguments)339 protected QueryContext CreateQueryContext(DataSourceSelectArguments arguments) { 340 IDictionary<string, object> whereParameters = WhereParameters.ToDictionary(_context, _owner); 341 IOrderedDictionary orderByParameters = OrderByParameters.GetValues(_context, _owner).ToCaseInsensitiveDictionary(); 342 IDictionary<string, object> orderGroupsByParameters = OrderGroupsByParameters.ToDictionary(_context, _owner); 343 IDictionary<string, object> selectNewParameters = SelectNewParameters.ToDictionary(_context, _owner); 344 IDictionary<string, object> groupByParameters = GroupByParameters.ToDictionary(_context, _owner); 345 346 return new QueryContext( 347 whereParameters, 348 orderGroupsByParameters, 349 orderByParameters, 350 groupByParameters, 351 selectNewParameters, 352 arguments); 353 } 354 355 /// <summary> 356 /// Creates a select expression based on parameters and properties 357 /// </summary> BuildQuery(DataSourceSelectArguments arguments)358 protected virtual IQueryable BuildQuery(DataSourceSelectArguments arguments) { 359 if (arguments == null) { 360 throw new ArgumentNullException("arguments"); 361 } 362 363 // Create the query context 364 QueryContext context = CreateQueryContext(arguments); 365 366 // Clear out old values before selecting new data 367 _originalValues = null; 368 369 // Get the source of the query(root IQueryable) 370 object result = GetSource(context); 371 372 if (result != null) { 373 IQueryable source = QueryableDataSourceHelper.AsQueryable(result); 374 // Apply additional filterting 375 return ExecuteQuery(source, context); 376 } 377 return null; 378 } 379 ExecuteQuery(IQueryable source, QueryContext context)380 protected virtual IQueryable ExecuteQuery(IQueryable source, QueryContext context) { 381 // Execute Query 382 source = ExecuteQueryExpressions(source, context); 383 384 // Execute Sorting 385 source = ExecuteSorting(source, context); 386 387 // Execute Paging 388 source = ExecutePaging(source, context); 389 390 return source; 391 } 392 ExecuteQueryExpressions(IQueryable source, QueryContext context)393 protected IQueryable ExecuteQueryExpressions(IQueryable source, QueryContext context) { 394 if (source != null) { 395 QueryCreatedEventArgs queryArgs = new QueryCreatedEventArgs(source); 396 OnQueryCreated(queryArgs); 397 source = queryArgs.Query ?? source; 398 399 // Support the Dynamic Expression language used by LinqDataSource 400 if (AutoGenerateWhereClause) { 401 if (!String.IsNullOrEmpty(Where)) { 402 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, 403 AtlasWeb.LinqDataSourceView_WhereAlreadySpecified, _owner.ID)); 404 } 405 source = QueryableDataSourceHelper.CreateWhereExpression(context.WhereParameters, source, _queryable); 406 } 407 else if (!String.IsNullOrEmpty(Where)) { 408 source = _queryable.Where(source, Where, context.WhereParameters.ToEscapedParameterKeys(_owner)); 409 } 410 411 if (AutoGenerateOrderByClause) { 412 if (!String.IsNullOrEmpty(OrderBy)) { 413 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, 414 AtlasWeb.LinqDataSourceView_OrderByAlreadySpecified, _owner.ID)); 415 } 416 source = QueryableDataSourceHelper.CreateOrderByExpression(context.OrderByParameters, source, _queryable); 417 } 418 else if (!String.IsNullOrEmpty(OrderBy)) { 419 source = _queryable.OrderBy(source, OrderBy, context.OrderByParameters.ToEscapedParameterKeys(_owner)); 420 } 421 422 423 string groupBy = GroupBy; 424 if (String.IsNullOrEmpty(groupBy)) { 425 if (!String.IsNullOrEmpty(OrderGroupsBy)) { 426 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, 427 AtlasWeb.LinqDataSourceView_OrderGroupsByRequiresGroupBy, _owner.ID)); 428 } 429 } 430 else { 431 source = _queryable.GroupBy(source, groupBy, "it", context.GroupByParameters.ToEscapedParameterKeys(_owner)); 432 if (!String.IsNullOrEmpty(OrderGroupsBy)) { 433 source = _queryable.OrderBy(source, OrderGroupsBy, context.OrderGroupsByParameters.ToEscapedParameterKeys(_owner)); 434 } 435 } 436 437 if (!String.IsNullOrEmpty(SelectNew)) { 438 source = _queryable.Select(source, SelectNew, context.SelectParameters.ToEscapedParameterKeys(_owner)); 439 } 440 441 return source; 442 } 443 444 return source; 445 } 446 ExecuteSorting(IQueryable source, QueryContext context)447 protected IQueryable ExecuteSorting(IQueryable source, QueryContext context) { 448 string sortExpression = context.Arguments.SortExpression; 449 450 if (CanSort && AutoSort && !String.IsNullOrEmpty(sortExpression)) { 451 source = _queryable.OrderBy(source, sortExpression); 452 } 453 return source; 454 } 455 ExecutePaging(IQueryable source, QueryContext context)456 protected IQueryable ExecutePaging(IQueryable source, QueryContext context) { 457 if (CanPage && AutoPage) { 458 if (CanRetrieveTotalRowCount && context.Arguments.RetrieveTotalRowCount) { 459 context.Arguments.TotalRowCount = _queryable.Count(source); 460 } 461 462 if ((context.Arguments.MaximumRows > 0) && (context.Arguments.StartRowIndex >= 0)) { 463 source = _queryable.Skip(source, context.Arguments.StartRowIndex); 464 source = _queryable.Take(source, context.Arguments.MaximumRows); 465 } 466 } 467 else if (context.Arguments.RetrieveTotalRowCount && (context.Arguments.TotalRowCount == -1)) { 468 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, 469 AtlasWeb.LinqDataSourceView_PagingNotHandled, _owner.ID)); 470 } 471 return source; 472 } 473 LoadViewState(object savedState)474 protected virtual void LoadViewState(object savedState) { 475 if (savedState != null) { 476 object[] myState = (object[])savedState; 477 478 if (myState[0] != null) { 479 ((IStateManager)WhereParameters).LoadViewState(myState[0]); 480 } 481 if (myState[1] != null) { 482 ((IStateManager)OrderByParameters).LoadViewState(myState[1]); 483 } 484 if (myState[2] != null) { 485 ((IStateManager)GroupByParameters).LoadViewState(myState[2]); 486 } 487 if (myState[3] != null) { 488 ((IStateManager)OrderGroupsByParameters).LoadViewState(myState[3]); 489 } 490 if (myState[4] != null) { 491 ((IStateManager)SelectNewParameters).LoadViewState(myState[4]); 492 } 493 if (myState[5] != null) { 494 _originalValues = (Hashtable)myState[5]; 495 } 496 } 497 } 498 SaveViewState()499 protected virtual object SaveViewState() { 500 object[] myState = new object[6]; 501 myState[0] = DataSourceHelper.SaveViewState(_whereParameters); 502 myState[1] = DataSourceHelper.SaveViewState(_orderByParameters); 503 myState[2] = DataSourceHelper.SaveViewState(_groupByParameters); 504 myState[3] = DataSourceHelper.SaveViewState(_orderGroupsByParameters); 505 myState[4] = DataSourceHelper.SaveViewState(_selectNewParameters); 506 if ((_originalValues != null) && (_originalValues.Count > 0)) { 507 myState[5] = _originalValues; 508 } 509 510 return myState; 511 } 512 513 TrackViewState()514 protected virtual void TrackViewState() { 515 _isTracking = true; 516 DataSourceHelper.TrackViewState(_whereParameters); 517 DataSourceHelper.TrackViewState(_orderByParameters); 518 DataSourceHelper.TrackViewState(_groupByParameters); 519 DataSourceHelper.TrackViewState(_orderGroupsByParameters); 520 DataSourceHelper.TrackViewState(_selectNewParameters); 521 } 522 ExecuteSelect(DataSourceSelectArguments arguments)523 protected internal override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments) { 524 ClearOriginalValues(); 525 526 IQueryable source = BuildQuery(arguments); 527 528 IList results = source.ToList(source.ElementType); 529 530 // Store original values for concurrency 531 StoreOriginalValues(results); 532 533 return results; 534 } 535 ClearOriginalValues()536 protected void ClearOriginalValues() { 537 _originalValues = null; 538 } 539 GetOriginalValues(IDictionary keys)540 protected virtual IDictionary GetOriginalValues(IDictionary keys) { 541 // Table data is stored in a hashtable with column names for keys and an ArrayList of row data for values. 542 // i.e, Hashtable { ID = ArrayList { 0, 1, 2 }, Name = ArrayList { "A", "B", "C" } } 543 if (_originalValues != null) { 544 // matches list keeps track of row indexes which match. 545 List<bool> matches = new List<bool>(); 546 foreach (DictionaryEntry entry in keys) { 547 string propertyName = (String)entry.Key; 548 if (_originalValues.ContainsKey(propertyName)) { 549 object propertyValue = entry.Value; 550 // get the row values for the current column. 551 ArrayList values = (ArrayList)_originalValues[propertyName]; 552 for (int i = 0; i < values.Count; i++) { 553 if (matches.Count <= i) { // first column 554 matches.Add(OriginalValueMatches(values[i], propertyValue)); 555 } 556 else if (matches[i] == true) { // subsequent columns 557 matches[i] = OriginalValueMatches(values[i], propertyValue); 558 } 559 } 560 } 561 } 562 563 int rowIndex = matches.IndexOf(true); 564 // no rows match or too many rows match. 565 if ((rowIndex < 0) || (matches.IndexOf(true, rowIndex + 1) >= 0)) { 566 throw new InvalidOperationException(AtlasWeb.LinqDataSourceView_OriginalValuesNotFound); 567 } 568 // get original values for the matching row. 569 Dictionary<string, object> rowValues = new Dictionary<string, object>(_originalValues.Count, 570 StringComparer.OrdinalIgnoreCase); 571 foreach (DictionaryEntry entry in _originalValues) { 572 ArrayList value = (ArrayList)entry.Value; 573 rowValues.Add((string)entry.Key, value[rowIndex]); 574 } 575 return rowValues; 576 } 577 578 return null; 579 } 580 StoreOriginalValues(IList results)581 protected virtual void StoreOriginalValues(IList results) { 582 // Derived classes can override this function and call the overload 583 // to determine which columns it should store for optimistic concurrency 584 } 585 StoreOriginalValues(IList results, Func<PropertyDescriptor, bool> include)586 protected void StoreOriginalValues(IList results, Func<PropertyDescriptor, bool> include) { 587 PropertyDescriptorCollection props = TypeDescriptor.GetProperties(EntityType); 588 int numRows = results.Count; 589 int maxColumns = props.Count; 590 _originalValues = new Hashtable(maxColumns, StringComparer.OrdinalIgnoreCase); 591 foreach (PropertyDescriptor p in props) { 592 if (include(p) && p.PropertyType.IsSerializable) { 593 ArrayList values = new ArrayList(numRows); 594 _originalValues[p.Name] = values; 595 foreach (object currentRow in results) { 596 values.Add(p.GetValue(currentRow)); 597 } 598 } 599 } 600 } 601 602 Update(IDictionary keys, IDictionary values, IDictionary oldValues)603 public int Update(IDictionary keys, IDictionary values, IDictionary oldValues) { 604 return ExecuteUpdate(keys, values, oldValues); 605 } 606 Delete(IDictionary keys, IDictionary oldValues)607 public int Delete(IDictionary keys, IDictionary oldValues) { 608 return ExecuteDelete(keys, oldValues); 609 } 610 Insert(IDictionary values)611 public int Insert(IDictionary values) { 612 return ExecuteInsert(values); 613 } 614 BuildDeleteObject(IDictionary keys, IDictionary oldValues, IDictionary<string, Exception> validationErrors)615 protected QueryableDataSourceEditData BuildDeleteObject(IDictionary keys, IDictionary oldValues, IDictionary<string, Exception> validationErrors) { 616 QueryableDataSourceEditData editData = new QueryableDataSourceEditData(); 617 Type dataObjectType = EntityType; 618 IDictionary caseInsensitiveOldValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase); 619 IDictionary originalValues = GetOriginalValues(keys); 620 621 ParameterCollection deleteParameters = DeleteParameters; 622 if (!DataSourceHelper.MergeDictionaries(dataObjectType, 623 deleteParameters, 624 keys, 625 caseInsensitiveOldValues, 626 validationErrors)) { 627 return editData; 628 } 629 630 if (!DataSourceHelper.MergeDictionaries(dataObjectType, 631 deleteParameters, 632 oldValues, 633 caseInsensitiveOldValues, 634 validationErrors)) { 635 return editData; 636 637 } 638 639 if (originalValues != null) { 640 if (!DataSourceHelper.MergeDictionaries(dataObjectType, 641 deleteParameters, 642 originalValues, 643 caseInsensitiveOldValues, 644 validationErrors)) { 645 return editData; 646 } 647 } 648 649 editData.OriginalDataObject = DataSourceHelper.BuildDataObject(dataObjectType, caseInsensitiveOldValues, validationErrors); 650 return editData; 651 } 652 BuildInsertObject(IDictionary values, IDictionary<string, Exception> validationErrors)653 protected QueryableDataSourceEditData BuildInsertObject(IDictionary values, IDictionary<string, Exception> validationErrors) { 654 QueryableDataSourceEditData editData = new QueryableDataSourceEditData(); 655 Type dataObjectType = EntityType; 656 IDictionary caseInsensitiveNewValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase); 657 658 if (!DataSourceHelper.MergeDictionaries(dataObjectType, 659 InsertParameters, 660 InsertParameters.GetValues(_context, _owner), 661 caseInsensitiveNewValues, 662 validationErrors)) { 663 return editData; 664 } 665 666 if (!DataSourceHelper.MergeDictionaries(dataObjectType, 667 InsertParameters, 668 values, 669 caseInsensitiveNewValues, validationErrors)) { 670 return editData; 671 } 672 673 editData.NewDataObject = DataSourceHelper.BuildDataObject(dataObjectType, caseInsensitiveNewValues, validationErrors); 674 return editData; 675 } 676 BuildUpdateObjects(IDictionary keys, IDictionary values, IDictionary oldValues, IDictionary<string, Exception> validationErrors)677 protected QueryableDataSourceEditData BuildUpdateObjects(IDictionary keys, IDictionary values, IDictionary oldValues, IDictionary<string, Exception> validationErrors) { 678 QueryableDataSourceEditData editData = new QueryableDataSourceEditData(); 679 Type dataObjectType = EntityType; 680 IDictionary caseInsensitiveNewValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase); 681 IDictionary caseInsensitiveOldValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase); 682 IDictionary originalValues = GetOriginalValues(keys); 683 684 // We start out with the old values, just to pre-populate the list with items 685 // that might not have corresponding new values. For example if a GridView has 686 // a read-only field, there will be an old value, but no new value. The data object 687 // still has to have *some* value for a given field, so we just use the old value. 688 ParameterCollection updateParameters = UpdateParameters; 689 690 // If we have validation errors bail out while merging bailout 691 692 if (!DataSourceHelper.MergeDictionaries(dataObjectType, 693 updateParameters, 694 oldValues, 695 caseInsensitiveOldValues, 696 caseInsensitiveNewValues, 697 validationErrors)) { 698 return editData; 699 } 700 701 if (!DataSourceHelper.MergeDictionaries(dataObjectType, 702 updateParameters, 703 keys, 704 caseInsensitiveOldValues, 705 caseInsensitiveNewValues, 706 validationErrors)) { 707 return editData; 708 } 709 710 if (originalValues != null) { 711 if (!DataSourceHelper.MergeDictionaries(dataObjectType, 712 updateParameters, 713 originalValues, 714 caseInsensitiveOldValues, 715 caseInsensitiveNewValues, 716 validationErrors)) { 717 return editData; 718 } 719 } 720 721 if (!DataSourceHelper.MergeDictionaries(dataObjectType, 722 updateParameters, 723 values, 724 caseInsensitiveNewValues, 725 validationErrors)) { 726 return editData; 727 } 728 729 editData.NewDataObject = DataSourceHelper.BuildDataObject(dataObjectType, caseInsensitiveNewValues, validationErrors); 730 731 if (editData.NewDataObject != null) { 732 editData.OriginalDataObject = DataSourceHelper.BuildDataObject(dataObjectType, caseInsensitiveOldValues, validationErrors); 733 } 734 return editData; 735 } 736 DeleteObject(object oldEntity)737 protected virtual int DeleteObject(object oldEntity) { 738 return 0; 739 } 740 UpdateObject(object oldEntity, object newEntity)741 protected virtual int UpdateObject(object oldEntity, object newEntity) { 742 return 0; 743 } 744 InsertObject(object newEntity)745 protected virtual int InsertObject(object newEntity) { 746 return 0; 747 } 748 HandleValidationErrors(IDictionary<string, Exception> errors, DataSourceOperation operation)749 protected abstract void HandleValidationErrors(IDictionary<string, Exception> errors, DataSourceOperation operation); 750 ExecuteDelete(IDictionary keys, IDictionary oldValues)751 protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues) { 752 IDictionary<string, Exception> errors = new Dictionary<string, Exception>(StringComparer.OrdinalIgnoreCase); 753 QueryableDataSourceEditData editData = BuildDeleteObject(keys, oldValues, errors); 754 755 if (errors.Any()) { 756 HandleValidationErrors(errors, DataSourceOperation.Delete); 757 } 758 else { 759 return DeleteObject(editData.OriginalDataObject); 760 } 761 762 return -1; 763 } 764 ExecuteInsert(IDictionary values)765 protected override int ExecuteInsert(IDictionary values) { 766 IDictionary<string, Exception> errors = new Dictionary<string, Exception>(StringComparer.OrdinalIgnoreCase); 767 QueryableDataSourceEditData editData = BuildInsertObject(values, errors); 768 if (errors.Any()) { 769 HandleValidationErrors(errors, DataSourceOperation.Insert); 770 } 771 else { 772 return InsertObject(editData.NewDataObject); 773 } 774 return -1; 775 } 776 ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)777 protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues) { 778 IDictionary<string, Exception> errors = new Dictionary<string, Exception>(StringComparer.OrdinalIgnoreCase); 779 QueryableDataSourceEditData editData = BuildUpdateObjects(keys, values, oldValues, errors); 780 if (errors.Any()) { 781 HandleValidationErrors(errors, DataSourceOperation.Update); 782 } 783 else { 784 return UpdateObject(editData.OriginalDataObject, editData.NewDataObject); 785 } 786 return -1; 787 } 788 789 public event EventHandler<QueryCreatedEventArgs> QueryCreated { 790 add { 791 Events.AddHandler(EventQueryCreated, value); 792 } 793 remove { 794 Events.RemoveHandler(EventQueryCreated, value); 795 } 796 } 797 798 [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")] OnQueryCreated(QueryCreatedEventArgs e)799 protected virtual void OnQueryCreated(QueryCreatedEventArgs e) { 800 EventHandler<QueryCreatedEventArgs> handler = (EventHandler<QueryCreatedEventArgs>)Events[EventQueryCreated]; 801 if (handler != null) { 802 handler(this, e); 803 } 804 } 805 OriginalValueMatches(object originalValue, object value)806 private bool OriginalValueMatches(object originalValue, object value) { 807 // NOTE: Comparing IEnumerable contents instead of instances to ensure that 808 // timestamp columns (of type byte[]) can be matched appropriately. 809 IEnumerable originalValueEnumerable = originalValue as IEnumerable; 810 IEnumerable valueEnumerable = value as IEnumerable; 811 if ((originalValueEnumerable != null) && (valueEnumerable != null)) { 812 return QueryableDataSourceHelper.EnumerableContentEquals(originalValueEnumerable, valueEnumerable); 813 } 814 return originalValue.Equals(value); 815 } 816 817 #region IStateManager Members 818 819 bool IStateManager.IsTrackingViewState { 820 get { return IsTrackingViewState; } 821 } 822 IStateManager.LoadViewState(object state)823 void IStateManager.LoadViewState(object state) { 824 LoadViewState(state); 825 } 826 IStateManager.SaveViewState()827 object IStateManager.SaveViewState() { 828 return SaveViewState(); 829 } 830 IStateManager.TrackViewState()831 void IStateManager.TrackViewState() { 832 TrackViewState(); 833 } 834 835 #endregion 836 } 837 } 838