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