/// <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)); }
/// <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); } } }