Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Client / System / Data / Services / Client / Binding / DataServiceCollectionOfT.cs / 1305376 / DataServiceCollectionOfT.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // //// DataServiceCollection class // // //--------------------------------------------------------------------- namespace System.Data.Services.Client { #region Namespaces. using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; #endregion Namespaces. ////// Enumeration used for cunstructing DataServiceCollection which specifies /// whether the collection should be in automatic change tracking mode or in manual (none) /// change tracking mode. /// public enum TrackingMode { ///The collection should not track changes. None, ///The collection should automatically track changes to the entities /// in the collection. AutoChangeTracking } ////// An DataServiceCollection is a collection of entites. /// The collection implement INotifyCollectionChanged and INotifyPropertyChanged. /// ///An entity class public class DataServiceCollection: ObservableCollection { #region Private fields. /// The BindingObserver associated with the DataServiceCollection private BindingObserver observer; ///Is this a root collection private bool rootCollection; ///The continuation for partial collections. private DataServiceQueryContinuationcontinuation; /// True if tracking setup was deferred to first Load() call. private bool trackingOnLoad; ///Callback tracked until tracking is enabled. private FuncentityChangedCallback; /// Callback tracked until tracking is enabled. private FunccollectionChangedCallback; /// Entity set name tracked until tracking is enabled. private string entitySetName; #if ASTORIA_LIGHT ///If there's an async operation in progress (LoadAsync methods), this field is true /// otherwise it's false. private bool asyncOperationInProgress; #endif #endregion Private fields. ////// Creates a default data service collection, with auto-change tracking enabled /// as soon as data is loaded into it. /// public DataServiceCollection() : this(null, null, TrackingMode.AutoChangeTracking, null, null, null) { } ////// Creates a tracking DataServiceCollection and pre-loads it /// /// Collection to initialize the new DataServiceCollection with. public DataServiceCollection(IEnumerableitems) : this(null, items, TrackingMode.AutoChangeTracking, null, null, null) { } /// /// Creates a tracking DataServiceCollection and pre-loads it, enabling or disabling auto-change tracking as needed /// /// Collection to initialize the new DataServiceCollection with. /// Whether auto-change tracking should be enabled public DataServiceCollection(IEnumerableitems, TrackingMode trackingMode) : this(null, items, trackingMode, null, null, null) { } /// /// Creates a data service collection associated to the provided context for /// the purpose of auto-change tracking. /// /// DataServiceContext associated with the new collection. public DataServiceCollection(DataServiceContext context) : this(context, null, TrackingMode.AutoChangeTracking, null, null, null) { } ///Creates a new DataServiceCollection. /// DataServiceContext associated with the new collection. /// The entity set of the elements in the collection. /// Delegate that would get called when an entity changes. /// Delegate that would get called when an entity collection changes. public DataServiceCollection( DataServiceContext context, string entitySetName, FuncentityChangedCallback, Func collectionChangedCallback) : this(context, null, TrackingMode.AutoChangeTracking, entitySetName, entityChangedCallback, collectionChangedCallback) { } /// Creates a new DataServiceCollection. /// Enumeration of items to initialize the new DataServiceCollection with. /// The tracking mode for the new collection. /// The name of the entity set the elements in the collection belong to. /// Delegate that gets called when an entity changes. /// Delegate that gets called when an entity collection changes. public DataServiceCollection( IEnumerableitems, TrackingMode trackingMode, string entitySetName, Func entityChangedCallback, Func collectionChangedCallback) : this(null, items, trackingMode, entitySetName, entityChangedCallback, collectionChangedCallback) { } /// Creates a new DataServiceCollection. ///associated with the new collection. /// Enumeration of items to initialize the new DataServiceCollection with. /// The tracking mode for the new collection. /// The name of the entity set the elements in the collection belong to. /// Delegate that gets called when an entity changes. /// Delegate that gets called when an entity collection changes. public DataServiceCollection( DataServiceContext context, IEnumerable items, TrackingMode trackingMode, string entitySetName, Func entityChangedCallback, Func collectionChangedCallback) { if (trackingMode == TrackingMode.AutoChangeTracking) { if (context == null) { if (items == null) { // Enable tracking on first Load/LoadAsync call, when we can obtain a context this.trackingOnLoad = true; // Save off these for when we enable tracking later this.entitySetName = entitySetName; this.entityChangedCallback = entityChangedCallback; this.collectionChangedCallback = collectionChangedCallback; } else { // This throws if no context can be obtained, no need to check here context = DataServiceCollection .GetContextFromItems(items); } } if (!this.trackingOnLoad) { if (items != null) { DataServiceCollection .ValidateIteratorParameter(items); } this.StartTracking(context, items, entitySetName, entityChangedCallback, collectionChangedCallback); } } else if (items != null) { this.Load(items); } } /// Creates new DataServiceCollection. /// The materializer ///associated with the new collection. /// Enumeration of items to initialize the new DataServiceCollection with. /// The tracking mode for the new collection. /// The name of the entity set the elements in the collection belong to. /// Delegate that gets called when an entity changes. /// Delegate that gets called when an entity collection changes. /// This is the internal constructor called from materializer and used inside our projection queries. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800", Justification = "Constructor and debug-only code can't reuse cast.")] internal DataServiceCollection( object atomMaterializer, DataServiceContext context, IEnumerableitems, TrackingMode trackingMode, string entitySetName, Func entityChangedCallback, Func collectionChangedCallback) : this( context != null ? context : ((AtomMaterializer)atomMaterializer).Context, items, trackingMode, entitySetName, entityChangedCallback, collectionChangedCallback) { Debug.Assert(atomMaterializer != null, "atomMaterializer != null"); Debug.Assert(((AtomMaterializer)atomMaterializer).Context != null, "Context != null"); if (items != null) { ((AtomMaterializer)atomMaterializer).PropagateContinuation(items, this); } } #region Properties /// The continuation for additional results; null if none are available. public DataServiceQueryContinuationContinuation { get { return this.continuation; } set { this.continuation = value; } } /// Observer for the collection. ///The setter would get called only for child collections in the graph. internal BindingObserver Observer { get { return this.observer; } set { Debug.Assert(!this.rootCollection, "Must be a child collection to have the Observer setter called."); Debug.Assert(typeof(System.ComponentModel.INotifyPropertyChanged).IsAssignableFrom(typeof(T)), "The entity type must be trackable (by implementing INotifyPropertyChanged interface)"); this.observer = value; } } ////// Whether this collection is actively tracking /// internal bool IsTracking { get { return this.observer != null; } } #endregion ///Loads the collection from another collection. /// Collection whose elements will be loaded into the DataServiceCollection. ////// When tracking is enabled, the behavior of Load would be to attach all those entities that are not already tracked by the context /// associated with the collection. The operation will go deep into the input entities so that all related /// entities are attached to the context if not already present. All entities in public void Load(IEnumerable/// will be tracked after Load is done. /// Load method checks for duplication. The collection will ignore any duplicated items been loaded. /// For large amount of items, consider DataServiceContext.LoadProperty instead. /// items) { DataServiceCollection .ValidateIteratorParameter(items); if (this.trackingOnLoad) { // This throws if no context can be obtained, no need to check here DataServiceContext context = DataServiceCollection .GetContextFromItems(items); this.trackingOnLoad = false; this.StartTracking(context, items, this.entitySetName, this.entityChangedCallback, this.collectionChangedCallback); } else { this.StartLoading(); try { this.InternalLoadCollection(items); } finally { this.FinishLoading(); } } } #if ASTORIA_LIGHT /// A completion event for the ///, /// and method. This event is raised exactly once for each call to the public event EventHandler, /// or method. It is called both when the operation /// succeeded and/or when it failed. LoadCompleted; /// Loads the collection asynchronously from a DataServiceQuery instance. /// A query of type DataServiceQuery. ///This method uses the event-based async pattern. /// The method returns immediately without waiting for the query to complete. Then it calls the handler of the /// public void LoadAsync(IQueryableevent exactly once on the UI thread. The event will be raised regradless /// if the query succeeded or not. /// This class only support one asynchronous operation in flight. query) { Util.CheckArgumentNull(query, "query"); DataServiceQuery dsq = query as DataServiceQuery ; if (dsq == null) { throw new ArgumentException(Strings.DataServiceCollection_LoadAsyncRequiresDataServiceQuery, "query"); } if (this.asyncOperationInProgress) { throw new InvalidOperationException(Strings.DataServiceCollection_MultipleLoadAsyncOperationsAtTheSameTime); } if (this.trackingOnLoad) { this.StartTracking(((DataServiceQueryProvider)dsq.Provider).Context, null, this.entitySetName, this.entityChangedCallback, this.collectionChangedCallback); this.trackingOnLoad = false; } BeginLoadAsyncOperation( asyncCallback => dsq.BeginExecute(asyncCallback, null), asyncResult => { QueryOperationResponse response = (QueryOperationResponse )dsq.EndExecute(asyncResult); this.Load(response); return response; }); } /// Loads the collection asynchronously for a property represented by the DataServiceCollection. ///This method loads the content of a property represented by this DataServiceCollection. /// If this instance is not associated with any property and entity the method will fail. /// This method uses the event-based async pattern. /// The method returns immediately without waiting for the query to complete. Then it calls the handler of the /// public void LoadAsync() { if (!this.IsTracking) { throw new InvalidOperationException(Strings.DataServiceCollection_OperationForTrackedOnly); } object parent; string property; if (!this.observer.LookupParent(this, out parent, out property)) { throw new InvalidOperationException(Strings.DataServiceCollection_LoadAsyncNoParamsWithoutParentEntity); } if (this.asyncOperationInProgress) { throw new InvalidOperationException(Strings.DataServiceCollection_MultipleLoadAsyncOperationsAtTheSameTime); } BeginLoadAsyncOperation( asyncCallback => this.observer.Context.BeginLoadProperty(parent, property, asyncCallback, null), asyncResult => (QueryOperationResponse)this.observer.Context.EndLoadProperty(asyncResult)); } ///event exactly once on the UI thread. The event will be raised regradless /// if the query succeeded or not. /// This class only support one asynchronous operation in flight. Loads next partial set for this collection. ///If this collection doesn't have a continuation token (this.Continuation == null) then this method /// returns false and does not issue any request. /// If there is a continuation token the method will return true and will start a request to load /// the next partial set based on that continuation token. ///This method is the same as public bool LoadNextPartialSetAsync() { if (!this.IsTracking) { throw new InvalidOperationException(Strings.DataServiceCollection_OperationForTrackedOnly); } if (this.asyncOperationInProgress) { throw new InvalidOperationException(Strings.DataServiceCollection_MultipleLoadAsyncOperationsAtTheSameTime); } if (this.Continuation == null) { if (this.LoadCompleted != null) { this.LoadCompleted(this, new LoadCompletedEventArgs(null, null)); } return false; } BeginLoadAsyncOperation( asyncCallback => this.observer.Context.BeginExecute(this.Continuation, asyncCallback, null), asyncResult => { QueryOperationResponseexcept that it runs the query as defined /// by the continuation token of this collection. /// The method returns immediately without waiting for the query to complete. Then it calls the handler of the /// event exactly once on the UI thread. The event will be raised regradless /// if the query succeeded or not. Even if the method returns false, the event will be raised (immeditaly) /// This class only support one asynchronous operation in flight. response = (QueryOperationResponse )this.observer.Context.EndExecute (asyncResult); this.Load(response); return response; }); return true; } #endif /// Loads a single entity into the collection. /// Entity to be added. ////// When tracking is enabled, the behavior of Load would be to attach the entity if it is not already tracked by the context /// associated with the collection. The operation will go deep into the input entity so that all related /// entities are attached to the context if not already present. The public void Load(T item) { // When loading a single item, if (item == null) { throw Error.ArgumentNull("item"); } this.StartLoading(); try { if (!this.Contains(item)) { this.Add(item); } } finally { this.FinishLoading(); } } ///will be /// tracked after Load is done. /// Load method checks for duplication. The collection will ignore any duplicated items been loaded. /// /// Clears a collection and optionally detaches all the items in it including deep added items from the context /// /// true if detach from context must happen, false otherwise. This parameter only affects tracked collections. public void Clear(bool stopTracking) { if (!this.IsTracking) { throw new InvalidOperationException(Strings.DataServiceCollection_OperationForTrackedOnly); } if (!stopTracking) { // non-binding or just clear this.Clear(); } else { Debug.Assert(this.observer.Context != null, "Must have valid context when the collection is being observed."); try { this.observer.DetachBehavior = true; this.Clear(); } finally { this.observer.DetachBehavior = false; } } } ///Stop tracking the collection. The operation is only allowed for root collections. ////// All the entitities in the root collection and all it's related objects will be untracked at the /// end of this operation. /// public void Detach() { if (!this.IsTracking) { throw new InvalidOperationException(Strings.DataServiceCollection_OperationForTrackedOnly); } // Operation only allowed on root collections. if (!this.rootCollection) { throw new InvalidOperationException(Strings.DataServiceCollection_CannotStopTrackingChildCollection); } this.observer.StopTracking(); this.observer = null; this.rootCollection = false; } #if ASTORIA_LIGHT public new void Add(T item) { if (this.IsTracking) { INotifyPropertyChanged notify = item as INotifyPropertyChanged; if (notify == null) { throw new InvalidOperationException(Strings.DataBinding_NotifyPropertyChangedNotImpl(item.GetType())); } } base.Add(item); } #endif ////// Override to prevent additions to the collection in "deferred tracking" mode /// /// Index of element to add /// Element to add protected override void InsertItem(int index, T item) { if (this.trackingOnLoad) { throw new InvalidOperationException(Strings.DataServiceCollection_InsertIntoTrackedButNotLoadedCollection); } base.InsertItem(index, item); } ////// Verifies that input iterator parameter is not null and in case /// of Silverlight, it is not of DataServiceQuery type. /// /// Input iterator parameter. private static void ValidateIteratorParameter(IEnumerableitems) { Util.CheckArgumentNull(items, "items"); #if ASTORIA_LIGHT DataServiceQuery dsq = items as DataServiceQuery ; if (dsq != null) { throw new ArgumentException(Strings.DataServiceCollection_DataServiceQueryCanNotBeEnumerated); } #endif } /// /// Obtain the DataServiceContext from the incoming enumerable /// /// An IEnumerable that may be a DataServiceQuery or QueryOperationResponse object ///DataServiceContext instance associated with the input private static DataServiceContext GetContextFromItems(IEnumerableitems) { Debug.Assert(items != null, "items != null"); DataServiceQuery dataServiceQuery = items as DataServiceQuery ; if (dataServiceQuery != null) { DataServiceQueryProvider queryProvider = dataServiceQuery.Provider as DataServiceQueryProvider; Debug.Assert(queryProvider != null, "Got DataServiceQuery with unknown query provider."); DataServiceContext context = queryProvider.Context; Debug.Assert(context != null, "Query provider must always have valid context."); return context; } QueryOperationResponse queryOperationResponse = items as QueryOperationResponse; if (queryOperationResponse != null) { Debug.Assert(queryOperationResponse.Results != null, "Got QueryOperationResponse without valid results."); DataServiceContext context = queryOperationResponse.Results.Context; Debug.Assert(context != null, "Materializer must always have valid context."); return context; } throw new ArgumentException(Strings.DataServiceCollection_CannotDetermineContextFromItems); } /// /// Populate this collection with another collection of items /// /// The items to populate this collection with private void InternalLoadCollection(IEnumerableitems) { Debug.Assert(items != null, "items != null"); #if !ASTORIA_LIGHT // For SDP, we must execute the Query implicitly DataServiceQuery query = items as DataServiceQuery ; if (query != null) { items = query.Execute() as QueryOperationResponse ; } #else Debug.Assert(!(items is DataServiceQuery), "SL Client using DSQ as items...should have been caught by ValidateIteratorParameter."); #endif foreach (T item in items) { // if this is too slow, consider hashing the set // or just use LoadProperties if (!this.Contains(item)) { this.Add(item); } } QueryOperationResponse response = items as QueryOperationResponse ; if (response != null) { // this should never be throwing (since we've enumerated already)! // Note: Inner collection's nextPartLinkUri is set by the materializer this.continuation = response.GetContinuation(); } else { this.continuation = null; } } /// /// Prepare the collection for loading. For tracked collections, we enter the attaching state /// private void StartLoading() { if (this.IsTracking) { // Observer must be present on the target collection which implies that the operation would fail on default constructed objects. if (this.observer.Context == null) { throw new InvalidOperationException(Strings.DataServiceCollection_LoadRequiresTargetCollectionObserved); } this.observer.AttachBehavior = true; } } ////// Reset the collection after loading. For tracked collections, we exit the attaching state. /// private void FinishLoading() { if (this.IsTracking) { this.observer.AttachBehavior = false; } } ///Initialize and start tracking an DataServiceCollection /// The context /// Collection to initialize with /// The entity set of the elements in the collection. /// delegate that needs to be called when an entity changes. /// delegate that needs to be called when an entity collection is changed. private void StartTracking( DataServiceContext context, IEnumerableitems, String entitySet, Func entityChanged, Func collectionChanged) { Debug.Assert(context != null, "Must have a valid context to initialize."); Debug.Assert(this.observer == null, "Must have no observer which implies Initialize should only be called once."); // Add everything from the input collection. if (items != null) { this.InternalLoadCollection(items); } this.observer = new BindingObserver(context, entityChanged, collectionChanged); this.observer.StartTracking(this, entitySet); this.rootCollection = true; } #if ASTORIA_LIGHT /// Helper method to start a LoadAsync operation. /// Function which calls the Begin method for the load. It should take/// parameter which should be used as the callback for the Begin call. It should return /// of the started asynchronous operation (or throw). /// Function which calls the End method for the load. It should take /// which represents the asynchronous operation in flight. It should return /// with the result of the operation (or throw). /// The method takes care of error handling as well as maintaining the private void BeginLoadAsyncOperation( Func. /// Note that it does not check the to disallow multiple operations in flight. /// The method makes sure that the will be called from the UI thread. It makes no assumptions /// about the calling thread of this method. /// The method does not process the results of the , it just raises the /// event as appropriate. If there's some processing to be done for the results it should all be done by the /// method before it returns. beginCall, Func endCall) { Debug.Assert(!this.asyncOperationInProgress, "Trying to start a new LoadAsync while another is still in progress. We should have thrown."); // NOTE: this is Silverlight-only, use BackgroundWorker instead of Deployment.Current.Dispatcher // to do this in [....]/WCF once we decide to add it there as well. // Note that we must mark the operation as in progress before we actually call Begin // as the async operation might end immediately inside the Begin call and we have no control // over the ordering between the End callback thread, the thread Begin is called from // and the UI thread on which we process the end event. this.asyncOperationInProgress = true; try { IAsyncResult asyncResult = beginCall( ar => System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => { try { QueryOperationResponse result = endCall(ar); this.asyncOperationInProgress = false; if (this.LoadCompleted != null) { this.LoadCompleted(this, new LoadCompletedEventArgs(result, null)); } } catch (Exception ex) { this.asyncOperationInProgress = false; if (this.LoadCompleted != null) { this.LoadCompleted(this, new LoadCompletedEventArgs(null, ex)); } } })); } catch (Exception) { this.asyncOperationInProgress = false; throw; } } #endif } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // //// DataServiceCollection class // // //--------------------------------------------------------------------- namespace System.Data.Services.Client { #region Namespaces. using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; #endregion Namespaces. ////// Enumeration used for cunstructing DataServiceCollection which specifies /// whether the collection should be in automatic change tracking mode or in manual (none) /// change tracking mode. /// public enum TrackingMode { ///The collection should not track changes. None, ///The collection should automatically track changes to the entities /// in the collection. AutoChangeTracking } ////// An DataServiceCollection is a collection of entites. /// The collection implement INotifyCollectionChanged and INotifyPropertyChanged. /// ///An entity class public class DataServiceCollection: ObservableCollection { #region Private fields. /// The BindingObserver associated with the DataServiceCollection private BindingObserver observer; ///Is this a root collection private bool rootCollection; ///The continuation for partial collections. private DataServiceQueryContinuationcontinuation; /// True if tracking setup was deferred to first Load() call. private bool trackingOnLoad; ///Callback tracked until tracking is enabled. private FuncentityChangedCallback; /// Callback tracked until tracking is enabled. private FunccollectionChangedCallback; /// Entity set name tracked until tracking is enabled. private string entitySetName; #if ASTORIA_LIGHT ///If there's an async operation in progress (LoadAsync methods), this field is true /// otherwise it's false. private bool asyncOperationInProgress; #endif #endregion Private fields. ////// Creates a default data service collection, with auto-change tracking enabled /// as soon as data is loaded into it. /// public DataServiceCollection() : this(null, null, TrackingMode.AutoChangeTracking, null, null, null) { } ////// Creates a tracking DataServiceCollection and pre-loads it /// /// Collection to initialize the new DataServiceCollection with. public DataServiceCollection(IEnumerableitems) : this(null, items, TrackingMode.AutoChangeTracking, null, null, null) { } /// /// Creates a tracking DataServiceCollection and pre-loads it, enabling or disabling auto-change tracking as needed /// /// Collection to initialize the new DataServiceCollection with. /// Whether auto-change tracking should be enabled public DataServiceCollection(IEnumerableitems, TrackingMode trackingMode) : this(null, items, trackingMode, null, null, null) { } /// /// Creates a data service collection associated to the provided context for /// the purpose of auto-change tracking. /// /// DataServiceContext associated with the new collection. public DataServiceCollection(DataServiceContext context) : this(context, null, TrackingMode.AutoChangeTracking, null, null, null) { } ///Creates a new DataServiceCollection. /// DataServiceContext associated with the new collection. /// The entity set of the elements in the collection. /// Delegate that would get called when an entity changes. /// Delegate that would get called when an entity collection changes. public DataServiceCollection( DataServiceContext context, string entitySetName, FuncentityChangedCallback, Func collectionChangedCallback) : this(context, null, TrackingMode.AutoChangeTracking, entitySetName, entityChangedCallback, collectionChangedCallback) { } /// Creates a new DataServiceCollection. /// Enumeration of items to initialize the new DataServiceCollection with. /// The tracking mode for the new collection. /// The name of the entity set the elements in the collection belong to. /// Delegate that gets called when an entity changes. /// Delegate that gets called when an entity collection changes. public DataServiceCollection( IEnumerableitems, TrackingMode trackingMode, string entitySetName, Func entityChangedCallback, Func collectionChangedCallback) : this(null, items, trackingMode, entitySetName, entityChangedCallback, collectionChangedCallback) { } /// Creates a new DataServiceCollection. ///associated with the new collection. /// Enumeration of items to initialize the new DataServiceCollection with. /// The tracking mode for the new collection. /// The name of the entity set the elements in the collection belong to. /// Delegate that gets called when an entity changes. /// Delegate that gets called when an entity collection changes. public DataServiceCollection( DataServiceContext context, IEnumerable items, TrackingMode trackingMode, string entitySetName, Func entityChangedCallback, Func collectionChangedCallback) { if (trackingMode == TrackingMode.AutoChangeTracking) { if (context == null) { if (items == null) { // Enable tracking on first Load/LoadAsync call, when we can obtain a context this.trackingOnLoad = true; // Save off these for when we enable tracking later this.entitySetName = entitySetName; this.entityChangedCallback = entityChangedCallback; this.collectionChangedCallback = collectionChangedCallback; } else { // This throws if no context can be obtained, no need to check here context = DataServiceCollection .GetContextFromItems(items); } } if (!this.trackingOnLoad) { if (items != null) { DataServiceCollection .ValidateIteratorParameter(items); } this.StartTracking(context, items, entitySetName, entityChangedCallback, collectionChangedCallback); } } else if (items != null) { this.Load(items); } } /// Creates new DataServiceCollection. /// The materializer ///associated with the new collection. /// Enumeration of items to initialize the new DataServiceCollection with. /// The tracking mode for the new collection. /// The name of the entity set the elements in the collection belong to. /// Delegate that gets called when an entity changes. /// Delegate that gets called when an entity collection changes. /// This is the internal constructor called from materializer and used inside our projection queries. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800", Justification = "Constructor and debug-only code can't reuse cast.")] internal DataServiceCollection( object atomMaterializer, DataServiceContext context, IEnumerableitems, TrackingMode trackingMode, string entitySetName, Func entityChangedCallback, Func collectionChangedCallback) : this( context != null ? context : ((AtomMaterializer)atomMaterializer).Context, items, trackingMode, entitySetName, entityChangedCallback, collectionChangedCallback) { Debug.Assert(atomMaterializer != null, "atomMaterializer != null"); Debug.Assert(((AtomMaterializer)atomMaterializer).Context != null, "Context != null"); if (items != null) { ((AtomMaterializer)atomMaterializer).PropagateContinuation(items, this); } } #region Properties /// The continuation for additional results; null if none are available. public DataServiceQueryContinuationContinuation { get { return this.continuation; } set { this.continuation = value; } } /// Observer for the collection. ///The setter would get called only for child collections in the graph. internal BindingObserver Observer { get { return this.observer; } set { Debug.Assert(!this.rootCollection, "Must be a child collection to have the Observer setter called."); Debug.Assert(typeof(System.ComponentModel.INotifyPropertyChanged).IsAssignableFrom(typeof(T)), "The entity type must be trackable (by implementing INotifyPropertyChanged interface)"); this.observer = value; } } ////// Whether this collection is actively tracking /// internal bool IsTracking { get { return this.observer != null; } } #endregion ///Loads the collection from another collection. /// Collection whose elements will be loaded into the DataServiceCollection. ////// When tracking is enabled, the behavior of Load would be to attach all those entities that are not already tracked by the context /// associated with the collection. The operation will go deep into the input entities so that all related /// entities are attached to the context if not already present. All entities in public void Load(IEnumerable/// will be tracked after Load is done. /// Load method checks for duplication. The collection will ignore any duplicated items been loaded. /// For large amount of items, consider DataServiceContext.LoadProperty instead. /// items) { DataServiceCollection .ValidateIteratorParameter(items); if (this.trackingOnLoad) { // This throws if no context can be obtained, no need to check here DataServiceContext context = DataServiceCollection .GetContextFromItems(items); this.trackingOnLoad = false; this.StartTracking(context, items, this.entitySetName, this.entityChangedCallback, this.collectionChangedCallback); } else { this.StartLoading(); try { this.InternalLoadCollection(items); } finally { this.FinishLoading(); } } } #if ASTORIA_LIGHT /// A completion event for the ///, /// and method. This event is raised exactly once for each call to the public event EventHandler, /// or method. It is called both when the operation /// succeeded and/or when it failed. LoadCompleted; /// Loads the collection asynchronously from a DataServiceQuery instance. /// A query of type DataServiceQuery. ///This method uses the event-based async pattern. /// The method returns immediately without waiting for the query to complete. Then it calls the handler of the /// public void LoadAsync(IQueryableevent exactly once on the UI thread. The event will be raised regradless /// if the query succeeded or not. /// This class only support one asynchronous operation in flight. query) { Util.CheckArgumentNull(query, "query"); DataServiceQuery dsq = query as DataServiceQuery ; if (dsq == null) { throw new ArgumentException(Strings.DataServiceCollection_LoadAsyncRequiresDataServiceQuery, "query"); } if (this.asyncOperationInProgress) { throw new InvalidOperationException(Strings.DataServiceCollection_MultipleLoadAsyncOperationsAtTheSameTime); } if (this.trackingOnLoad) { this.StartTracking(((DataServiceQueryProvider)dsq.Provider).Context, null, this.entitySetName, this.entityChangedCallback, this.collectionChangedCallback); this.trackingOnLoad = false; } BeginLoadAsyncOperation( asyncCallback => dsq.BeginExecute(asyncCallback, null), asyncResult => { QueryOperationResponse response = (QueryOperationResponse )dsq.EndExecute(asyncResult); this.Load(response); return response; }); } /// Loads the collection asynchronously for a property represented by the DataServiceCollection. ///This method loads the content of a property represented by this DataServiceCollection. /// If this instance is not associated with any property and entity the method will fail. /// This method uses the event-based async pattern. /// The method returns immediately without waiting for the query to complete. Then it calls the handler of the /// public void LoadAsync() { if (!this.IsTracking) { throw new InvalidOperationException(Strings.DataServiceCollection_OperationForTrackedOnly); } object parent; string property; if (!this.observer.LookupParent(this, out parent, out property)) { throw new InvalidOperationException(Strings.DataServiceCollection_LoadAsyncNoParamsWithoutParentEntity); } if (this.asyncOperationInProgress) { throw new InvalidOperationException(Strings.DataServiceCollection_MultipleLoadAsyncOperationsAtTheSameTime); } BeginLoadAsyncOperation( asyncCallback => this.observer.Context.BeginLoadProperty(parent, property, asyncCallback, null), asyncResult => (QueryOperationResponse)this.observer.Context.EndLoadProperty(asyncResult)); } ///event exactly once on the UI thread. The event will be raised regradless /// if the query succeeded or not. /// This class only support one asynchronous operation in flight. Loads next partial set for this collection. ///If this collection doesn't have a continuation token (this.Continuation == null) then this method /// returns false and does not issue any request. /// If there is a continuation token the method will return true and will start a request to load /// the next partial set based on that continuation token. ///This method is the same as public bool LoadNextPartialSetAsync() { if (!this.IsTracking) { throw new InvalidOperationException(Strings.DataServiceCollection_OperationForTrackedOnly); } if (this.asyncOperationInProgress) { throw new InvalidOperationException(Strings.DataServiceCollection_MultipleLoadAsyncOperationsAtTheSameTime); } if (this.Continuation == null) { if (this.LoadCompleted != null) { this.LoadCompleted(this, new LoadCompletedEventArgs(null, null)); } return false; } BeginLoadAsyncOperation( asyncCallback => this.observer.Context.BeginExecute(this.Continuation, asyncCallback, null), asyncResult => { QueryOperationResponseexcept that it runs the query as defined /// by the continuation token of this collection. /// The method returns immediately without waiting for the query to complete. Then it calls the handler of the /// event exactly once on the UI thread. The event will be raised regradless /// if the query succeeded or not. Even if the method returns false, the event will be raised (immeditaly) /// This class only support one asynchronous operation in flight. response = (QueryOperationResponse )this.observer.Context.EndExecute (asyncResult); this.Load(response); return response; }); return true; } #endif /// Loads a single entity into the collection. /// Entity to be added. ////// When tracking is enabled, the behavior of Load would be to attach the entity if it is not already tracked by the context /// associated with the collection. The operation will go deep into the input entity so that all related /// entities are attached to the context if not already present. The public void Load(T item) { // When loading a single item, if (item == null) { throw Error.ArgumentNull("item"); } this.StartLoading(); try { if (!this.Contains(item)) { this.Add(item); } } finally { this.FinishLoading(); } } ///will be /// tracked after Load is done. /// Load method checks for duplication. The collection will ignore any duplicated items been loaded. /// /// Clears a collection and optionally detaches all the items in it including deep added items from the context /// /// true if detach from context must happen, false otherwise. This parameter only affects tracked collections. public void Clear(bool stopTracking) { if (!this.IsTracking) { throw new InvalidOperationException(Strings.DataServiceCollection_OperationForTrackedOnly); } if (!stopTracking) { // non-binding or just clear this.Clear(); } else { Debug.Assert(this.observer.Context != null, "Must have valid context when the collection is being observed."); try { this.observer.DetachBehavior = true; this.Clear(); } finally { this.observer.DetachBehavior = false; } } } ///Stop tracking the collection. The operation is only allowed for root collections. ////// All the entitities in the root collection and all it's related objects will be untracked at the /// end of this operation. /// public void Detach() { if (!this.IsTracking) { throw new InvalidOperationException(Strings.DataServiceCollection_OperationForTrackedOnly); } // Operation only allowed on root collections. if (!this.rootCollection) { throw new InvalidOperationException(Strings.DataServiceCollection_CannotStopTrackingChildCollection); } this.observer.StopTracking(); this.observer = null; this.rootCollection = false; } #if ASTORIA_LIGHT public new void Add(T item) { if (this.IsTracking) { INotifyPropertyChanged notify = item as INotifyPropertyChanged; if (notify == null) { throw new InvalidOperationException(Strings.DataBinding_NotifyPropertyChangedNotImpl(item.GetType())); } } base.Add(item); } #endif ////// Override to prevent additions to the collection in "deferred tracking" mode /// /// Index of element to add /// Element to add protected override void InsertItem(int index, T item) { if (this.trackingOnLoad) { throw new InvalidOperationException(Strings.DataServiceCollection_InsertIntoTrackedButNotLoadedCollection); } base.InsertItem(index, item); } ////// Verifies that input iterator parameter is not null and in case /// of Silverlight, it is not of DataServiceQuery type. /// /// Input iterator parameter. private static void ValidateIteratorParameter(IEnumerableitems) { Util.CheckArgumentNull(items, "items"); #if ASTORIA_LIGHT DataServiceQuery dsq = items as DataServiceQuery ; if (dsq != null) { throw new ArgumentException(Strings.DataServiceCollection_DataServiceQueryCanNotBeEnumerated); } #endif } /// /// Obtain the DataServiceContext from the incoming enumerable /// /// An IEnumerable that may be a DataServiceQuery or QueryOperationResponse object ///DataServiceContext instance associated with the input private static DataServiceContext GetContextFromItems(IEnumerableitems) { Debug.Assert(items != null, "items != null"); DataServiceQuery dataServiceQuery = items as DataServiceQuery ; if (dataServiceQuery != null) { DataServiceQueryProvider queryProvider = dataServiceQuery.Provider as DataServiceQueryProvider; Debug.Assert(queryProvider != null, "Got DataServiceQuery with unknown query provider."); DataServiceContext context = queryProvider.Context; Debug.Assert(context != null, "Query provider must always have valid context."); return context; } QueryOperationResponse queryOperationResponse = items as QueryOperationResponse; if (queryOperationResponse != null) { Debug.Assert(queryOperationResponse.Results != null, "Got QueryOperationResponse without valid results."); DataServiceContext context = queryOperationResponse.Results.Context; Debug.Assert(context != null, "Materializer must always have valid context."); return context; } throw new ArgumentException(Strings.DataServiceCollection_CannotDetermineContextFromItems); } /// /// Populate this collection with another collection of items /// /// The items to populate this collection with private void InternalLoadCollection(IEnumerableitems) { Debug.Assert(items != null, "items != null"); #if !ASTORIA_LIGHT // For SDP, we must execute the Query implicitly DataServiceQuery query = items as DataServiceQuery ; if (query != null) { items = query.Execute() as QueryOperationResponse ; } #else Debug.Assert(!(items is DataServiceQuery), "SL Client using DSQ as items...should have been caught by ValidateIteratorParameter."); #endif foreach (T item in items) { // if this is too slow, consider hashing the set // or just use LoadProperties if (!this.Contains(item)) { this.Add(item); } } QueryOperationResponse response = items as QueryOperationResponse ; if (response != null) { // this should never be throwing (since we've enumerated already)! // Note: Inner collection's nextPartLinkUri is set by the materializer this.continuation = response.GetContinuation(); } else { this.continuation = null; } } /// /// Prepare the collection for loading. For tracked collections, we enter the attaching state /// private void StartLoading() { if (this.IsTracking) { // Observer must be present on the target collection which implies that the operation would fail on default constructed objects. if (this.observer.Context == null) { throw new InvalidOperationException(Strings.DataServiceCollection_LoadRequiresTargetCollectionObserved); } this.observer.AttachBehavior = true; } } ////// Reset the collection after loading. For tracked collections, we exit the attaching state. /// private void FinishLoading() { if (this.IsTracking) { this.observer.AttachBehavior = false; } } ///Initialize and start tracking an DataServiceCollection /// The context /// Collection to initialize with /// The entity set of the elements in the collection. /// delegate that needs to be called when an entity changes. /// delegate that needs to be called when an entity collection is changed. private void StartTracking( DataServiceContext context, IEnumerableitems, String entitySet, Func entityChanged, Func collectionChanged) { Debug.Assert(context != null, "Must have a valid context to initialize."); Debug.Assert(this.observer == null, "Must have no observer which implies Initialize should only be called once."); // Add everything from the input collection. if (items != null) { this.InternalLoadCollection(items); } this.observer = new BindingObserver(context, entityChanged, collectionChanged); this.observer.StartTracking(this, entitySet); this.rootCollection = true; } #if ASTORIA_LIGHT /// Helper method to start a LoadAsync operation. /// Function which calls the Begin method for the load. It should take/// parameter which should be used as the callback for the Begin call. It should return /// of the started asynchronous operation (or throw). /// Function which calls the End method for the load. It should take /// which represents the asynchronous operation in flight. It should return /// with the result of the operation (or throw). /// The method takes care of error handling as well as maintaining the private void BeginLoadAsyncOperation( Func. /// Note that it does not check the to disallow multiple operations in flight. /// The method makes sure that the will be called from the UI thread. It makes no assumptions /// about the calling thread of this method. /// The method does not process the results of the , it just raises the /// event as appropriate. If there's some processing to be done for the results it should all be done by the /// method before it returns. beginCall, Func endCall) { Debug.Assert(!this.asyncOperationInProgress, "Trying to start a new LoadAsync while another is still in progress. We should have thrown."); // NOTE: this is Silverlight-only, use BackgroundWorker instead of Deployment.Current.Dispatcher // to do this in [....]/WCF once we decide to add it there as well. // Note that we must mark the operation as in progress before we actually call Begin // as the async operation might end immediately inside the Begin call and we have no control // over the ordering between the End callback thread, the thread Begin is called from // and the UI thread on which we process the end event. this.asyncOperationInProgress = true; try { IAsyncResult asyncResult = beginCall( ar => System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => { try { QueryOperationResponse result = endCall(ar); this.asyncOperationInProgress = false; if (this.LoadCompleted != null) { this.LoadCompleted(this, new LoadCompletedEventArgs(result, null)); } } catch (Exception ex) { this.asyncOperationInProgress = false; if (this.LoadCompleted != null) { this.LoadCompleted(this, new LoadCompletedEventArgs(null, ex)); } } })); } catch (Exception) { this.asyncOperationInProgress = false; throw; } } #endif } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ImageCodecInfo.cs
- HttpRequestWrapper.cs
- EventEntry.cs
- PersonalizationEntry.cs
- CursorEditor.cs
- MonitoringDescriptionAttribute.cs
- NavigationExpr.cs
- BamlRecordReader.cs
- SequentialWorkflowRootDesigner.cs
- documentsequencetextcontainer.cs
- ToolBar.cs
- DataObjectMethodAttribute.cs
- HTTPNotFoundHandler.cs
- FailedToStartupUIException.cs
- AssemblyUtil.cs
- SmiMetaDataProperty.cs
- SelectorAutomationPeer.cs
- RadioButtonFlatAdapter.cs
- CodePageEncoding.cs
- WSSecureConversation.cs
- PersonalizationProvider.cs
- Root.cs
- GenericXmlSecurityToken.cs
- Compiler.cs
- ClassHandlersStore.cs
- SchemaCollectionPreprocessor.cs
- DataGridViewCellCancelEventArgs.cs
- TabControl.cs
- storepermissionattribute.cs
- AsynchronousChannelMergeEnumerator.cs
- QueueAccessMode.cs
- SqlNamer.cs
- XamlWrappingReader.cs
- Point4D.cs
- AssemblyAssociatedContentFileAttribute.cs
- ExpressionBuilder.cs
- EditorZone.cs
- InvalidOleVariantTypeException.cs
- ListDictionaryInternal.cs
- BatchParser.cs
- Wizard.cs
- RegistryDataKey.cs
- RemotingConfigParser.cs
- SelectionWordBreaker.cs
- GeometryValueSerializer.cs
- _AutoWebProxyScriptEngine.cs
- UrlAuthorizationModule.cs
- iisPickupDirectory.cs
- IsolatedStoragePermission.cs
- StoreContentChangedEventArgs.cs
- ErrorHandlingReceiver.cs
- AssemblyAssociatedContentFileAttribute.cs
- ActivityValidationServices.cs
- InternalMappingException.cs
- CodeCompileUnit.cs
- SecurityUtils.cs
- KeyInterop.cs
- SchemaHelper.cs
- UIPermission.cs
- ActiveXHost.cs
- DoubleConverter.cs
- ObjectKeyFrameCollection.cs
- BuiltInExpr.cs
- DataGridViewImageColumn.cs
- DPAPIProtectedConfigurationProvider.cs
- XmlDataSourceDesigner.cs
- PageCache.cs
- DiscreteKeyFrames.cs
- GlyphElement.cs
- TextEffect.cs
- OutputChannelBinder.cs
- QilUnary.cs
- SqlParameterCollection.cs
- InstallerTypeAttribute.cs
- ObjectStorage.cs
- DataGridViewMethods.cs
- ControlCollection.cs
- RuntimeWrappedException.cs
- srgsitem.cs
- GeneralTransform.cs
- ConfigurationErrorsException.cs
- RequestSecurityToken.cs
- ServiceOperationHelpers.cs
- BuildProvider.cs
- InternalCache.cs
- LinqMaximalSubtreeNominator.cs
- ParserHooks.cs
- AvTrace.cs
- ServerValidateEventArgs.cs
- StringFormat.cs
- HyperLink.cs
- TraceHandler.cs
- TextEffect.cs
- FileFormatException.cs
- AnnotationDocumentPaginator.cs
- ConfigXmlDocument.cs
- SqlDataSourceCommandEventArgs.cs
- recordstatescratchpad.cs
- MimeReflector.cs
- URLString.cs