1 //---------------------------------------------------------------------
2 // <copyright file="ObjectViewQueryResultData.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.ComponentModel;
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;
20 namespace System.Data.Objects
23 /// Manages a binding list constructed from query results.
25 /// <typeparam name="TElement">
26 /// Type of the elements in the binding list.
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).
33 internal sealed class ObjectViewQueryResultData<TElement> : IObjectViewData<TElement>
35 private List<TElement> _bindingList;
38 /// ObjectContext used to add or delete objects when the list can be modified.
40 private ObjectContext _objectContext;
43 /// If the TElement type is an Entity type of some kind,
44 /// this field specifies the entity set to add entity objects.
46 private EntitySet _entitySet;
48 private bool _canEditItems;
49 private bool _canModifyList;
52 /// Construct a new instance of the ObjectViewQueryResultData class using the supplied query results.
54 /// <param name="queryResults">
55 /// Result of object query execution used to populate the binding list.
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.
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.
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.
70 internal ObjectViewQueryResultData(IEnumerable queryResults, ObjectContext objectContext, bool forceReadOnlyList, EntitySet entitySet)
72 bool canTrackItemChanges = IsEditable(typeof(TElement));
74 _objectContext = objectContext;
75 _entitySet = entitySet;
77 _canEditItems = canTrackItemChanges;
78 _canModifyList = !forceReadOnlyList && canTrackItemChanges && _objectContext != null;
80 _bindingList = new List<TElement>();
81 foreach (TElement element in queryResults)
83 _bindingList.Add(element);
88 /// Cannot be a DbDataRecord or a derivative of DbDataRecord
90 /// <param name="elementType"></param>
91 /// <returns></returns>
92 private bool IsEditable(Type elementType)
94 return !((elementType == typeof(DbDataRecord)) ||
95 ((elementType != typeof(DbDataRecord)) && elementType.IsSubclassOf(typeof(DbDataRecord))));
99 /// Throw an exception is an entity set was not specified for this instance.
101 private void EnsureEntitySet()
103 if (_entitySet == null)
105 throw EntityUtil.CannotResolveTheEntitySetforGivenEntity(typeof(TElement));
109 #region IObjectViewData<T> Members
111 public IList<TElement> List
113 get { return _bindingList; }
118 get { return _canModifyList && _entitySet != null; }
121 public bool AllowEdit
123 get { return _canEditItems; }
126 public bool AllowRemove
128 get { return _canModifyList; }
131 public bool FiresEventOnAdd
133 get { return false; }
136 public bool FiresEventOnRemove
141 public bool FiresEventOnClear
143 get { return false; }
146 public void EnsureCanAddNew()
151 public int Add(TElement item, bool isAddNew)
155 Debug.Assert(_objectContext != null, "ObjectContext is null.");
157 // If called for AddNew operation, add item to binding list, pending addition to ObjectContext.
160 _objectContext.AddObject(TypeHelpers.GetFullName(_entitySet), item);
163 _bindingList.Add(item);
165 return _bindingList.Count - 1;
168 public void CommitItemAt(int index)
172 Debug.Assert(_objectContext != null, "ObjectContext is null.");
174 TElement item = _bindingList[index];
175 _objectContext.AddObject(TypeHelpers.GetFullName(_entitySet), item);
180 while (0 < _bindingList.Count)
182 TElement entity = _bindingList[_bindingList.Count - 1];
184 Remove(entity, false);
188 public bool Remove(TElement item, bool isCancelNew)
192 Debug.Assert(_objectContext != null, "ObjectContext is null.");
196 // Item was previously added to binding list, but not ObjectContext.
197 removed = _bindingList.Remove(item);
201 EntityEntry stateEntry = _objectContext.ObjectStateManager.FindEntityEntry(item);
203 if (stateEntry != null)
206 // OnCollectionChanged event will be fired, where the binding list will be updated.
218 public ListChangedEventArgs OnCollectionChanged(object sender, CollectionChangeEventArgs e, ObjectViewListener listener)
220 ListChangedEventArgs changeArgs = null;
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)))
227 TElement item = (TElement)e.Element;
228 int itemIndex = _bindingList.IndexOf(item);
230 if (itemIndex >= 0) // Ignore entities that we don't know about.
232 // Only process "remove" events.
233 Debug.Assert(e.Action != CollectionChangeAction.Refresh, "Cache should never fire with refresh, it does not have clear");
235 if (e.Action == CollectionChangeAction.Remove)
237 _bindingList.Remove(item);
239 listener.UnregisterEntityEvents(item);
241 changeArgs = new ListChangedEventArgs(ListChangedType.ItemDeleted, itemIndex /* newIndex*/, -1 /* oldIndex*/);