Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / ObjectViewQueryResultData.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ObjectViewQueryResultData.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System;
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.ComponentModel;
13 using System.Data;
14 using System.Data.Common;
15 using System.Data.Metadata;
16 using System.Data.Metadata.Edm;
17 using System.Data.Objects.DataClasses;
18 using System.Diagnostics;
19
20 namespace System.Data.Objects
21 {
22     /// <summary>
23     /// Manages a binding list constructed from query results.
24     /// </summary>
25     /// <typeparam name="TElement">
26     /// Type of the elements in the binding list.
27     /// </typeparam>
28     /// <remarks>
29     /// The binding list is initialized from query results.
30     /// If the binding list can be modified, 
31     /// objects are added or removed from the ObjectStateManager (via the ObjectContext).
32     /// </remarks>
33     internal sealed class ObjectViewQueryResultData<TElement> : IObjectViewData<TElement>
34     {
35         private List<TElement> _bindingList;
36
37         /// <summary>
38         /// ObjectContext used to add or delete objects when the list can be modified.
39         /// </summary>
40         private ObjectContext _objectContext;
41         
42         /// <summary>
43         /// If the TElement type is an Entity type of some kind,
44         /// this field specifies the entity set to add entity objects.
45         /// </summary>
46         private EntitySet _entitySet; 
47
48         private bool _canEditItems;
49         private bool _canModifyList;
50
51         /// <summary>
52         /// Construct a new instance of the ObjectViewQueryResultData class using the supplied query results.
53         /// </summary>
54         /// <param name="queryResults">
55         /// Result of object query execution used to populate the binding list.
56         /// </param>
57         /// <param name="objectContext">
58         /// ObjectContext used to add or remove items.
59         /// If the binding list can be modified, this parameter should not be null.
60         /// </param>
61         /// <param name="forceReadOnlyList">
62         /// <b>True</b> if items should not be allowed to be added or removed from the binding list.
63         /// Note that other conditions may prevent the binding list from being modified, so a value of <b>false</b>
64         /// supplied for this parameter doesn't necessarily mean that the list will be writable.
65         /// </param>
66         /// <param name="entitySet">
67         /// If the TElement type is an Entity type of some kind,
68         /// this field specifies the entity set to add entity objects.
69         /// </param>
70         internal ObjectViewQueryResultData(IEnumerable queryResults, ObjectContext objectContext, bool forceReadOnlyList, EntitySet entitySet)
71         {
72             bool canTrackItemChanges = IsEditable(typeof(TElement));
73
74             _objectContext = objectContext;
75             _entitySet = entitySet;
76
77             _canEditItems = canTrackItemChanges;
78             _canModifyList = !forceReadOnlyList && canTrackItemChanges && _objectContext != null;
79
80             _bindingList = new List<TElement>();
81             foreach (TElement element in queryResults)
82             {
83                 _bindingList.Add(element);
84             }
85         }
86
87         /// <summary>
88         /// Cannot be a DbDataRecord or a derivative of DbDataRecord
89         /// </summary>
90         /// <param name="elementType"></param>
91         /// <returns></returns>
92         private bool IsEditable(Type elementType)
93         {
94             return !((elementType == typeof(DbDataRecord)) ||
95                      ((elementType != typeof(DbDataRecord)) && elementType.IsSubclassOf(typeof(DbDataRecord))));
96         }
97
98         /// <summary>
99         /// Throw an exception is an entity set was not specified for this instance.
100         /// </summary>
101         private void EnsureEntitySet()
102         {
103             if (_entitySet == null)
104             {
105                 throw EntityUtil.CannotResolveTheEntitySetforGivenEntity(typeof(TElement));
106             }
107         }
108
109         #region IObjectViewData<T> Members
110
111         public IList<TElement> List
112         {
113             get { return _bindingList; }
114         }
115
116         public bool AllowNew
117         {
118             get { return _canModifyList && _entitySet != null; }
119         }
120
121         public bool AllowEdit
122         {
123             get { return _canEditItems; }
124         }
125
126         public bool AllowRemove
127         {
128             get { return _canModifyList; }
129         }
130
131         public bool FiresEventOnAdd
132         {
133             get { return false; }
134         }
135
136         public bool FiresEventOnRemove
137         {
138             get { return true; }
139         }
140
141         public bool FiresEventOnClear
142         {
143             get { return false; }
144         }
145
146         public void EnsureCanAddNew()
147         {
148             EnsureEntitySet();
149         }
150
151         public int Add(TElement item, bool isAddNew)
152         {
153             EnsureEntitySet();
154
155             Debug.Assert(_objectContext != null, "ObjectContext is null.");
156             
157             // If called for AddNew operation, add item to binding list, pending addition to ObjectContext.
158             if (!isAddNew)
159             {
160                 _objectContext.AddObject(TypeHelpers.GetFullName(_entitySet), item);
161             }
162
163             _bindingList.Add(item);
164
165             return _bindingList.Count - 1;
166         }
167
168         public void CommitItemAt(int index)
169         {
170             EnsureEntitySet();
171
172             Debug.Assert(_objectContext != null, "ObjectContext is null.");
173
174             TElement item = _bindingList[index];
175             _objectContext.AddObject(TypeHelpers.GetFullName(_entitySet), item);
176         }
177
178         public void Clear()
179         {
180             while (0 < _bindingList.Count)
181             {
182                 TElement entity = _bindingList[_bindingList.Count - 1];
183
184                 Remove(entity, false);
185             }
186         }
187
188         public bool Remove(TElement item, bool isCancelNew)
189         {
190             bool removed;
191
192             Debug.Assert(_objectContext != null, "ObjectContext is null.");
193
194             if (isCancelNew)
195             {
196                 // Item was previously added to binding list, but not ObjectContext.
197                 removed = _bindingList.Remove(item);
198             }
199             else
200             {
201                 EntityEntry stateEntry = _objectContext.ObjectStateManager.FindEntityEntry(item);
202
203                 if (stateEntry != null)
204                 {
205                     stateEntry.Delete();
206                     // OnCollectionChanged event will be fired, where the binding list will be updated.
207                     removed = true;
208                 }
209                 else
210                 {
211                     removed = false;
212                 }
213             }
214
215             return removed;
216         }
217
218         public ListChangedEventArgs OnCollectionChanged(object sender, CollectionChangeEventArgs e, ObjectViewListener listener)
219         {
220             ListChangedEventArgs changeArgs = null;
221
222             // Since event is coming from cache and it might be shared amoung different queries
223             // we have to check to see if correct event is being handled.
224             if (e.Element.GetType().IsAssignableFrom(typeof(TElement)) &&
225                 _bindingList.Contains((TElement)(e.Element)))
226             {
227                 TElement item = (TElement)e.Element;
228                 int itemIndex = _bindingList.IndexOf(item);
229
230                 if (itemIndex >= 0) // Ignore entities that we don't know about.
231                 {
232                     // Only process "remove" events.
233                     Debug.Assert(e.Action != CollectionChangeAction.Refresh, "Cache should never fire with refresh, it does not have clear");
234
235                     if (e.Action == CollectionChangeAction.Remove)
236                     {
237                         _bindingList.Remove(item);
238
239                         listener.UnregisterEntityEvents(item);
240
241                         changeArgs = new ListChangedEventArgs(ListChangedType.ItemDeleted, itemIndex /* newIndex*/, -1 /* oldIndex*/);
242                     }
243                 }
244             }
245
246             return changeArgs;
247         }
248
249         #endregion
250     }
251 }