/// <summary>
        /// Adds the specified entity type and any associated entity types recursively to the specified set.
        /// </summary>
        /// <param name="entityType">The entity Type to add.</param>
        /// <param name="entityTypes">The types set to accumulate in.</param>
        /// <param name="metadataProvider">The metadata provider.</param>
        private static void AddEntityType(Type entityType, HashSet <Type> entityTypes, MetadataProvider metadataProvider)
        {
            if (entityTypes.Contains(entityType))
            {
                // already added this type
                return;
            }

            entityTypes.Add(entityType);
            RegisterDataControllerTypeDescriptionProvider(entityType, metadataProvider);

            foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(entityType))
            {
                // for any "exposed" association members, recursively add the associated
                // entity type
                if (pd.Attributes[typeof(AssociationAttribute)] != null && TypeUtility.IsDataMember(pd))
                {
                    Type includedEntityType = TypeUtility.GetElementType(pd.PropertyType);
                    if (metadataProvider.IsEntityType(entityType))
                    {
                        AddEntityType(includedEntityType, entityTypes, metadataProvider);
                    }
                }
            }

            // Recursively add any derived entity types specified by [KnownType]
            // attributes
            IEnumerable <Type> knownTypes = TypeUtility.GetKnownTypes(entityType, true);

            foreach (Type knownType in knownTypes)
            {
                if (entityType.IsAssignableFrom(knownType))
                {
                    AddEntityType(knownType, entityTypes, metadataProvider);
                }
            }
        }
        private static DataControllerDescription CreateDescription(HttpControllerDescriptor controllerDescriptor)
        {
            Type             dataControllerType = controllerDescriptor.ControllerType;
            MetadataProvider metadataProvider   = CreateMetadataProvider(dataControllerType);

            // get all public candidate methods and create the operations
            HashSet <Type> entityTypes                     = new HashSet <Type>();
            List <UpdateActionDescriptor> actions          = new List <UpdateActionDescriptor>();
            IEnumerable <MethodInfo>      methodsToInspect =
                dataControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public)
                .Where(p => (p.DeclaringType != typeof(DataController) && (p.DeclaringType != typeof(object))) && !p.IsSpecialName);

            foreach (MethodInfo method in methodsToInspect)
            {
                if (method.GetCustomAttributes(typeof(NonActionAttribute), false).Length > 0)
                {
                    continue;
                }

                if (method.IsVirtual && method.GetBaseDefinition().DeclaringType == typeof(DataController))
                {
                    // don't want to infer overrides of DataController virtual methods as
                    // operations
                    continue;
                }

                // We need to ensure the buddy metadata provider is registered BEFORE we
                // attempt to do convention, since we rely on IsEntity which relies on
                // KeyAttributes being present (possibly from "buddy" classes)
                RegisterAssociatedMetadataProvider(method);

                ChangeOperation operationType = ClassifyUpdateOperation(method, metadataProvider);
                if (operationType != ChangeOperation.None)
                {
                    Type entityType = method.GetParameters()[0].ParameterType;
                    UpdateActionDescriptor actionDescriptor = new UpdateActionDescriptor(controllerDescriptor, method, entityType, operationType);
                    ValidateAction(actionDescriptor);
                    actions.Add(actionDescriptor);

                    // TODO : currently considering entity types w/o any query methods
                    // exposing them. Should we?
                    if (metadataProvider.IsEntityType(entityType))
                    {
                        AddEntityType(entityType, entityTypes, metadataProvider);
                    }
                }
                else
                {
                    // if the method is a "query" operation returning an entity,
                    // add to entity types
                    if (method.ReturnType != typeof(void))
                    {
                        Type returnType  = TypeUtility.UnwrapTaskInnerType(method.ReturnType);
                        Type elementType = TypeUtility.GetElementType(returnType);
                        if (metadataProvider.IsEntityType(elementType))
                        {
                            AddEntityType(elementType, entityTypes, metadataProvider);
                        }
                    }
                }
            }

            return(new DataControllerDescription(dataControllerType, entityTypes, actions));
        }
Exemplo n.º 3
0
        /// <summary>
        /// Adds the specified associated entities to the specified association member for the specified entity.
        /// </summary>
        /// <param name="entity">The entity</param>
        /// <param name="associationProperty">The association member (singleton or collection)</param>
        /// <param name="associatedEntities">Collection of associated entities</param>
        private static void SetAssociationMember(object entity, PropertyDescriptor associationProperty, IEnumerable <object> associatedEntities)
        {
            if (associatedEntities.Count() == 0)
            {
                return;
            }

            object associationValue = associationProperty.GetValue(entity);

            if (typeof(IEnumerable).IsAssignableFrom(associationProperty.PropertyType))
            {
                if (associationValue == null)
                {
                    throw Error.InvalidOperation(Resource.DataController_AssociationCollectionPropertyIsNull, associationProperty.ComponentType.Name, associationProperty.Name);
                }

                IList list = associationValue as IList;
                IEnumerable <object> associationSequence = null;
                MethodInfo           addMethod           = null;
                if (list == null)
                {
                    // not an IList, so we have to use reflection
                    Type associatedEntityType = TypeUtility.GetElementType(associationValue.GetType());
                    addMethod = associationValue.GetType().GetMethod("Add", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { associatedEntityType }, null);
                    if (addMethod == null)
                    {
                        throw Error.InvalidOperation(Resource.DataController_InvalidCollectionMember, associationProperty.Name);
                    }
                    associationSequence = ((IEnumerable)associationValue).Cast <object>();
                }

                foreach (object associatedEntity in associatedEntities)
                {
                    // add the entity to the collection if it's not already there
                    if (list != null)
                    {
                        if (!list.Contains(associatedEntity))
                        {
                            list.Add(associatedEntity);
                        }
                    }
                    else
                    {
                        if (!associationSequence.Contains(associatedEntity))
                        {
                            addMethod.Invoke(associationValue, new object[] { associatedEntity });
                        }
                    }
                }
            }
            else
            {
                // set the reference if it's not already set
                object associatedEntity = associatedEntities.Single();
                object currentValue     = associationProperty.GetValue(entity);
                if (!Object.Equals(currentValue, associatedEntity))
                {
                    associationProperty.SetValue(entity, associatedEntity);
                }
            }
        }