/// <summary>
        /// Tries to get the value of a property and corresponding BindingPropertyInfo or ClientPropertyAnnotation if the property exists
        /// </summary>
        /// <param name="source">Source object whose property needs to be read</param>
        /// <param name="sourceProperty">Name of the source object property</param>
        /// <param name="model">The client model.</param>
        /// <param name="bindingPropertyInfo">BindingPropertyInfo corresponding to <paramref name="sourceProperty"/></param>
        /// <param name="clientProperty">Instance of ClientProperty corresponding to <paramref name="sourceProperty"/></param>
        /// <param name="propertyValue">Value of the property</param>
        /// <returns>true if the property exists and the value was read; otherwise false.</returns>
        internal static bool TryGetPropertyValue(object source, string sourceProperty, ClientEdmModel model, out BindingPropertyInfo bindingPropertyInfo, out ClientPropertyAnnotation clientProperty, out object propertyValue)
        {
            Type sourceType = source.GetType();

            bindingPropertyInfo = BindingEntityInfo.GetObservableProperties(sourceType, model)
                                  .SingleOrDefault(x => x.PropertyInfo.PropertyName == sourceProperty);

            bool propertyFound = bindingPropertyInfo != null;

            // bindingPropertyInfo is null for primitive properties.
            if (!propertyFound)
            {
                clientProperty = BindingEntityInfo.GetClientType(sourceType, model)
                                 .GetProperty(sourceProperty, UndeclaredPropertyBehavior.Support);

                propertyFound = clientProperty != null;
                if (!propertyFound)
                {
                    propertyValue = null;
                }
                else
                {
                    propertyValue = clientProperty.GetValue(source);
                }
            }
            else
            {
                clientProperty = null;
                propertyValue  = bindingPropertyInfo.PropertyInfo.GetValue(source);
            }

            return(propertyFound);
        }
Exemple #2
0
        /// <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);
        }
Exemple #3
0
        /// <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);
        }
Exemple #5
0
        /// <summary>Initialize and start tracking an DataServiceCollection</summary>
        /// <param name="context">The context</param>
        /// <param name="items">Collection to initialize with</param>
        /// <param name="entitySet">The entity set of the elements in the collection.</param>
        /// <param name="entityChanged">delegate that needs to be called when an entity changes.</param>
        /// <param name="collectionChanged">delegate that needs to be called when an entity collection is changed.</param>
        private void StartTracking(
            DataServiceContext context,
            IEnumerable <T> items,
            String entitySet,
            Func <EntityChangedParams, bool> entityChanged,
            Func <EntityCollectionChangedParams, bool> 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.");

            context.UsingDataServiceCollection = true;

            // Verify that T corresponds to an entity type.
            // Validate here before any items are added to the collection because if this fails we want to prevent the collection from being populated.
            if (!BindingEntityInfo.IsEntityType(typeof(T), context.Model))
            {
                throw new ArgumentException(Strings.DataBinding_DataServiceCollectionArgumentMustHaveEntityType(typeof(T)));
            }

            // Set up the observer first because we want the collection to know it's supposed to be tracked while the items are being loaded.
            // Items being added to the collection are not added to the binding graph until later when StartTracking is called on the observer.
            this.observer = new BindingObserver(context, entityChanged, collectionChanged);

            // Add everything from the input collection.
            if (items != null)
            {
                try
                {
                    this.InternalLoadCollection(items);
                }
                catch
                {
                    // If any failures occur, reset the observer since we couldn't successfully start tracking
                    this.observer = null;
                    throw;
                }
            }

            this.observer.StartTracking(this, entitySet);

            this.rootCollection = true;
        }
        /// <summary>
        /// Get the entity set name for the target entity object.
        /// </summary>
        /// <param name="target">An entity object.</param>
        /// <param name="targetEntitySet">The 'currently known' entity set name for the target object.</param>
        /// <param name="model">The client model.</param>
        /// <returns>The entity set name for the target object.</returns>
        /// <remarks>
        /// Allow user code to provide the entity set name. If user code does not provide the entity set name, then
        /// this method will get the entity set name from the value of the EntitySetAttribute.
        /// The 'currently known' entity set name for top level collections can be provided through OEC constructor
        /// </remarks>
        internal static string GetEntitySet(
            object target,
            string targetEntitySet,
            ClientEdmModel model)
        {
            Debug.Assert(target != null, "Argument 'target' cannot be null.");
            Debug.Assert(BindingEntityInfo.IsEntityType(target.GetType(), model), "Argument 'target' must be an entity type.");

            // Here's the rules in order of priority for resolving entity set name
            // 1. EntitySet name passed in the constructor or extension methods of DataServiceCollection
            // 2. EntitySet name specified in the EntitySet attribute by the code gen. {Remember this attribute is
            //    not generated in case of MEST)
            if (!String.IsNullOrEmpty(targetEntitySet))
            {
                return(targetEntitySet);
            }
            else
            {
                // If there is not a 'currently known' entity set name to validate against, then there must be
                // EntitySet attribute on the entity type
                return(BindingEntityInfo.GetEntitySetAttribute(target.GetType(), model));
            }
        }
        /// <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);
        }