private object GetCollectionInstance(ClientPropertyAnnotation property, out bool instanceCreated) { instanceCreated = false; object obj2 = property.GetValue(this.entity); if (obj2 != null) { return(obj2); } instanceCreated = true; Type propertyType = property.PropertyType; if (BindingEntityInfo.IsDataServiceCollection(propertyType, base.RequestInfo.MaxProtocolVersion)) { object[] args = new object[2]; args[1] = TrackingMode.None; return(Activator.CreateInstance(WebUtil.GetDataServiceCollectionOfT(new Type[] { property.EntityCollectionItemType }), args)); } Type c = typeof(List <>).MakeGenericType(new Type[] { property.EntityCollectionItemType }); if (!propertyType.IsAssignableFrom(c)) { c = propertyType; } return(Activator.CreateInstance(c)); }
internal static void VerifyObserverNotPresent <T>(object oec, string sourceProperty, Type sourceType) { Debug.Assert(BindingEntityInfo.IsDataServiceCollection(oec.GetType()), "Must be an DataServiceCollection."); DataServiceCollection <T> typedCollection = oec as DataServiceCollection <T>; if (typedCollection.Observer != null) { throw new InvalidOperationException(Strings.DataBinding_CollectionPropertySetterValueHasObserver(sourceProperty, sourceType)); } }
/// <summary> /// Determine if the specified type is an entity type. /// </summary> /// <param name="type">An object type specifier.</param> /// <param name="model">The client model.</param> /// <returns>true if the type is an entity type; otherwise false.</returns> internal static bool IsEntityType(Type type, ClientEdmModel model) { Debug.Assert(type != null, "Argument 'type' cannot be null."); metadataCacheLock.EnterReadLock(); try { if (knownNonEntityTypes.Contains(type)) { return(false); } } finally { metadataCacheLock.ExitReadLock(); } bool isEntityType; try { if (BindingEntityInfo.IsDataServiceCollection(type, model)) { return(false); } isEntityType = ClientTypeUtil.TypeOrElementTypeIsEntity(type); } catch (InvalidOperationException) { isEntityType = false; } if (!isEntityType) { metadataCacheLock.EnterWriteLock(); try { if (!knownNonEntityTypes.Contains(type)) { knownNonEntityTypes.Add(type); } } finally { metadataCacheLock.ExitWriteLock(); } } return(isEntityType); }
/// <summary> /// Determine if the specified type is an entity type. /// </summary> /// <param name="type">An object type specifier.</param> /// <returns>True if the type is an entity type; otherwise false.</returns> internal static bool IsEntityType(Type type) { Debug.Assert(type != null, "Argument 'type' cannot be null."); metadataCacheLock.EnterReadLock(); try { if (knownNonEntityTypes.Contains(type)) { return(false); } } finally { metadataCacheLock.ExitReadLock(); } try { if (BindingEntityInfo.IsDataServiceCollection(type)) { return(false); } return(ClientType.Create(type).IsEntityType); } catch (InvalidOperationException) { metadataCacheLock.EnterWriteLock(); try { if (!knownNonEntityTypes.Contains(type)) { knownNonEntityTypes.Add(type); } } finally { metadataCacheLock.ExitWriteLock(); } return(false); } }
internal void OnCollectionChanged(object collection, NotifyCollectionChangedEventArgs eventArgs) { Util.CheckArgumentNull(collection, "collection"); Util.CheckArgumentNull(eventArgs, "eventArgs"); Debug.Assert(BindingEntityInfo.IsDataServiceCollection(collection.GetType()), "We only register this event for DataServiceCollections."); #if DEBUG Debug.Assert(this.bindingGraph.IsTracking(collection), "Collection must be part of the graph if it has the event notification registered."); #endif object source; string sourceProperty; string sourceEntitySet; string targetEntitySet; this.bindingGraph.GetEntityCollectionInfo( collection, out source, out sourceProperty, out sourceEntitySet, out targetEntitySet); switch (eventArgs.Action) { case NotifyCollectionChangedAction.Add: this.OnAddToCollection( eventArgs, source, sourceProperty, targetEntitySet, collection); break; case NotifyCollectionChangedAction.Remove: this.OnDeleteFromCollection( eventArgs, source, sourceProperty, collection); break; case NotifyCollectionChangedAction.Replace: this.OnDeleteFromCollection( eventArgs, source, sourceProperty, collection); this.OnAddToCollection( eventArgs, source, sourceProperty, targetEntitySet, collection); break; case NotifyCollectionChangedAction.Reset: if (this.DetachBehavior) { this.RemoveWithDetachCollection(collection); } else { this.bindingGraph.RemoveCollection(collection); } break; #if !ASTORIA_LIGHT case NotifyCollectionChangedAction.Move: break; #endif default: throw new InvalidOperationException(Strings.DataBinding_CollectionChangedUnknownAction(eventArgs.Action)); } }
public bool AddCollection( object source, string sourceProperty, object collection, string collectionEntitySet) { Debug.Assert(collection != null, "'collection' can not be null"); Debug.Assert( BindingEntityInfo.IsDataServiceCollection(collection.GetType()), "Argument 'collection' must be an DataServiceCollection<T> of entity type T"); if (this.graph.ExistsVertex(collection)) { return(false); } Vertex collectionVertex = this.graph.AddVertex(collection); collectionVertex.IsCollection = true; collectionVertex.EntitySet = collectionEntitySet; ICollection collectionItf = collection as ICollection; if (source != null) { collectionVertex.Parent = this.graph.LookupVertex(source); collectionVertex.ParentProperty = sourceProperty; this.graph.AddEdge(source, collection, sourceProperty); Type entityType = BindingUtils.GetCollectionEntityType(collection.GetType()); Debug.Assert(entityType != null, "Collection must at least be inherited from DataServiceCollection<T>"); if (!typeof(INotifyPropertyChanged).IsAssignableFrom(entityType)) { throw new InvalidOperationException(Strings.DataBinding_NotifyPropertyChangedNotImpl(entityType)); } typeof(BindingGraph) .GetMethod("SetObserver", BindingFlags.Instance | BindingFlags.NonPublic) .MakeGenericMethod(entityType) .Invoke(this, new object[] { collectionItf }); } else { this.graph.Root = collectionVertex; } Debug.Assert( collectionVertex.Parent != null || collectionVertex.IsRootCollection, "If parent is null, then collectionVertex should be a root collection"); this.AttachCollectionNotification(collection); foreach (var item in collectionItf) { this.AddEntity( source, sourceProperty, item, collectionEntitySet, collection); } return(true); }
/// <summary>Obtain binding info corresponding to a given type</summary> /// <param name="entityType">Type for which to obtain information</param> /// <param name="model">The client model.</param> /// <returns>Info about the <paramref name="entityType"/></returns> private static BindingEntityInfoPerType GetBindingEntityInfoFor(Type entityType, ClientEdmModel model) { BindingEntityInfoPerType bindingEntityInfo; metadataCacheLock.EnterReadLock(); try { if (bindingEntityInfos.TryGetValue(entityType, out bindingEntityInfo)) { return(bindingEntityInfo); } } finally { metadataCacheLock.ExitReadLock(); } bindingEntityInfo = new BindingEntityInfoPerType(); // Try to get the entity set name from the EntitySetAttribute attributes. In order to make the // inheritance work, we need to look at the attributes declared in the base types also. // EntitySetAttribute does not allow multiples, so there can be at most 1 instance on the type. EntitySetAttribute entitySetAttribute = (EntitySetAttribute)entityType.GetCustomAttributes(typeof(EntitySetAttribute), true).SingleOrDefault(); // There must be exactly one (unambiguous) EntitySetAttribute attribute. bindingEntityInfo.EntitySet = entitySetAttribute != null ? entitySetAttribute.EntitySet : null; bindingEntityInfo.ClientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityType)); foreach (ClientPropertyAnnotation p in bindingEntityInfo.ClientType.Properties()) { BindingPropertyInfo bpi = null; Type propertyType = p.PropertyType; if (p.IsStreamLinkProperty) { // DataServiceStreamLink is not mutable externally // It implements INPC to notify controls about our updates // We should ignore its events since we are the only one updating it. continue; } else if (p.IsPrimitiveOrComplexCollection) { Debug.Assert(!BindingEntityInfo.IsDataServiceCollection(propertyType, model), "DataServiceCollection cannot be the type that backs collections of primitives or complex types."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection }; } else if (p.IsEntityCollection) { if (BindingEntityInfo.IsDataServiceCollection(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindDataServiceCollection }; } } else if (BindingEntityInfo.IsEntityType(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindEntity }; } else if (BindingEntityInfo.CanBeComplexType(propertyType)) { // Add complex types and nothing else. Debug.Assert(!p.IsKnownType, "Known types do not implement INotifyPropertyChanged."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindComplex }; } if (bpi != null) { bpi.PropertyInfo = p; // For entity types, all of the above types of properties are interesting. // For complex types, only observe collections and complex type properties. if (bindingEntityInfo.ClientType.IsEntityType || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindComplex || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection) { bindingEntityInfo.ObservableProperties.Add(bpi); } } } metadataCacheLock.EnterWriteLock(); try { if (!bindingEntityInfos.ContainsKey(entityType)) { bindingEntityInfos[entityType] = bindingEntityInfo; } } finally { metadataCacheLock.ExitWriteLock(); } return(bindingEntityInfo); }
/// <summary>Obtain binding info corresponding to a given type</summary> /// <param name="entityType">Type for which to obtain information</param> /// <returns>Info about the <paramref name="entityType"/></returns> private static BindingEntityInfoPerType GetBindingEntityInfoFor(Type entityType) { BindingEntityInfoPerType bindingEntityInfo; metadataCacheLock.EnterReadLock(); try { if (bindingEntityInfos.TryGetValue(entityType, out bindingEntityInfo)) { return(bindingEntityInfo); } } finally { metadataCacheLock.ExitReadLock(); } bindingEntityInfo = new BindingEntityInfoPerType(); // Try to get the entity set name from the EntitySetAttribute attributes. In order to make the // inheritance work, we need to look at the attributes declared in the base types also. object[] attributes = entityType.GetCustomAttributes(typeof(EntitySetAttribute), true); // There must be exactly one (unambiguous) EntitySetAttribute attribute. bindingEntityInfo.EntitySet = (attributes != null && attributes.Length == 1) ? ((EntitySetAttribute)attributes[0]).EntitySet : null; bindingEntityInfo.ClientType = ClientType.Create(entityType); foreach (ClientType.ClientProperty p in bindingEntityInfo.ClientType.Properties) { BindingPropertyInfo bpi = null; Type propertyType = p.PropertyType; if (p.CollectionType != null) { if (BindingEntityInfo.IsDataServiceCollection(propertyType)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindCollection }; } } else if (BindingEntityInfo.IsEntityType(propertyType)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindEntity }; } else if (BindingEntityInfo.CanBeComplexProperty(p)) { // Add complex types and nothing else. bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindComplex }; } if (bpi != null) { bpi.PropertyInfo = p; // For complex types only treat complex typed properties as observable, we are not going to observer entity typed or primitive properties. if (bindingEntityInfo.ClientType.IsEntityType || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindComplex) { bindingEntityInfo.ObservableProperties.Add(bpi); } } } metadataCacheLock.EnterWriteLock(); try { if (!bindingEntityInfos.ContainsKey(entityType)) { bindingEntityInfos[entityType] = bindingEntityInfo; } } finally { metadataCacheLock.ExitWriteLock(); } return(bindingEntityInfo); }
/// <summary>Handle changes to tracked DataServiceCollection.</summary> /// <param name="collection">The DataServiceCollection that raised the event.</param> /// <param name="eventArgs">Information about the event such as added/removed entities, operation.</param> internal void OnCollectionChanged(object collection, NotifyCollectionChangedEventArgs eventArgs) { Util.CheckArgumentNull(collection, "collection"); Util.CheckArgumentNull(eventArgs, "eventArgs"); Debug.Assert(BindingEntityInfo.IsDataServiceCollection(collection.GetType()), "We only register this event for DataServiceCollections."); #if DEBUG Debug.Assert(this.bindingGraph.IsTracking(collection), "Collection must be part of the graph if it has the event notification registered."); #endif object source; string sourceProperty; string sourceEntitySet; string targetEntitySet; this.bindingGraph.GetEntityCollectionInfo( collection, out source, out sourceProperty, out sourceEntitySet, out targetEntitySet); switch (eventArgs.Action) { case NotifyCollectionChangedAction.Add: // This event is raised by ObservableCollection.InsertItem. this.OnAddToCollection( eventArgs, source, sourceProperty, targetEntitySet, collection); break; case NotifyCollectionChangedAction.Remove: // This event is raised by ObservableCollection.RemoveItem. this.OnDeleteFromCollection( eventArgs, source, sourceProperty, collection); break; case NotifyCollectionChangedAction.Replace: // This event is raised by ObservableCollection.SetItem. this.OnDeleteFromCollection( eventArgs, source, sourceProperty, collection); this.OnAddToCollection( eventArgs, source, sourceProperty, targetEntitySet, collection); break; case NotifyCollectionChangedAction.Reset: // This event is raised by ObservableCollection.Clear. if (this.DetachBehavior) { // Detach behavior requires going through each item and detaching it from context. this.RemoveWithDetachCollection(collection); } else { // Non-detach behavior requires only removing vertices of collection from graph. this.bindingGraph.RemoveCollection(collection); } break; #if !ASTORIA_LIGHT case NotifyCollectionChangedAction.Move: // Do Nothing. Added for completeness. break; #endif default: throw new InvalidOperationException(Strings.DataBinding_CollectionChangedUnknownAction(eventArgs.Action)); } }
private static BindingEntityInfoPerType GetBindingEntityInfoFor(Type entityType) { BindingEntityInfoPerType bindingEntityInfo; metadataCacheLock.EnterReadLock(); try { if (bindingEntityInfos.TryGetValue(entityType, out bindingEntityInfo)) { return(bindingEntityInfo); } } finally { metadataCacheLock.ExitReadLock(); } bindingEntityInfo = new BindingEntityInfoPerType(); object[] attributes = entityType.GetCustomAttributes(typeof(EntitySetAttribute), true); bindingEntityInfo.EntitySet = (attributes != null && attributes.Length == 1) ? ((EntitySetAttribute)attributes[0]).EntitySet : null; bindingEntityInfo.ClientType = ClientType.Create(entityType); foreach (ClientType.ClientProperty p in bindingEntityInfo.ClientType.Properties) { BindingPropertyInfo bpi = null; Type propertyType = p.PropertyType; if (p.CollectionType != null) { if (BindingEntityInfo.IsDataServiceCollection(propertyType)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindCollection }; } } else if (BindingEntityInfo.IsEntityType(propertyType)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindEntity }; } else if (BindingEntityInfo.CanBeComplexProperty(p)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindComplex }; } if (bpi != null) { bpi.PropertyInfo = p; if (bindingEntityInfo.ClientType.IsEntityType || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindComplex) { bindingEntityInfo.ObservableProperties.Add(bpi); } } } metadataCacheLock.EnterWriteLock(); try { if (!bindingEntityInfos.ContainsKey(entityType)) { bindingEntityInfos[entityType] = bindingEntityInfo; } } finally { metadataCacheLock.ExitWriteLock(); } return(bindingEntityInfo); }