/// <summary> /// Gets or creates a collection property on the specified <paramref name="instance"/>. /// </summary> /// <param name="instance">Instance on which to get/create the collection.</param> /// <param name="property">Collection property on the <paramref name="instance"/>.</param> /// <param name="forLoadProperty">Is this collection being created for LoadProperty scenario.</param> /// <returns> /// The collection corresponding to the specified <paramref name="property"/>; /// never null. /// </returns> private object GetOrCreateCollectionProperty(object instance, ClientPropertyAnnotation property, bool forLoadProperty) { Debug.Assert(instance != null, "instance != null"); Debug.Assert(property != null, "property != null"); Debug.Assert(property.IsResourceSet, "property.IsEntityCollection has to be true - otherwise property isn't a collection"); // NOTE: in V1, we would have instantiated nested objects before setting them. object result; result = property.GetValue(instance); if (result == null) { Type collectionType = property.PropertyType; // For backward compatibility we need to have different strategy of collection creation b/w // LoadProperty scenario versus regular collection creation scenario. if (forLoadProperty) { if (BindingEntityInfo.IsDataServiceCollection(collectionType, this.MaterializerContext.Model)) { Debug.Assert(WebUtil.GetDataServiceCollectionOfT(property.EntityCollectionItemType) != null, "DataServiceCollection<> must be available here."); // new DataServiceCollection<property.EntityCollectionItemType>(null, TrackingMode.None) result = Activator.CreateInstance( WebUtil.GetDataServiceCollectionOfT(property.EntityCollectionItemType), null, TrackingMode.None); } else { // Try List<> first because that's what we did in V1/V2, but use the actual property type if it doesn't support List<> Type listCollectionType = typeof(List <>).MakeGenericType(property.EntityCollectionItemType); if (collectionType.IsAssignableFrom(listCollectionType)) { collectionType = listCollectionType; } result = Activator.CreateInstance(collectionType); } } else { if (DSClient.PlatformHelper.IsInterface(collectionType)) { collectionType = typeof(System.Collections.ObjectModel.Collection <>).MakeGenericType(property.EntityCollectionItemType); } result = this.CreateNewInstance(property.EdmProperty.Type, collectionType); } // Update the property value on the instance. property.SetValue(instance, result, property.PropertyName, false /* add */); } Debug.Assert(result != null, "result != null -- otherwise GetOrCreateCollectionProperty didn't fall back to creation"); return(result); }
/// <summary>Verifies the absence of observer for an DataServiceCollection</summary> /// <typeparam name="T">Type of DataServiceCollection</typeparam> /// <param name="oec">Non-typed collection object</param> /// <param name="sourceProperty">Collection property of the source object which is being assigned to</param> /// <param name="sourceType">Type of the source object</param> internal static void VerifyObserverNotPresent <T>(object oec, string sourceProperty, Type sourceType) #endif { #if DEBUG Debug.Assert(BindingEntityInfo.IsDataServiceCollection(oec.GetType(), model), "Must be an DataServiceCollection."); #endif 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>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.IsPrimitiveOrEnumOrComplexCollection) { 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); }