/// <summary> /// Populates the member metadata for the given type /// </summary> /// <param name="resourceTypeCacheItem">Instance of ResourceTypeCacheItem containing the ResourceType and its metadata.</param> /// <param name="workspace">workspace containing the metadata information</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <param name="primitiveResourceTypeMap">Map of primitive types to use when building member metadata.</param> internal static void PopulateMemberMetadata( ResourceTypeCacheItem resourceTypeCacheItem, IProviderMetadata workspace, ProviderMetadataCacheItem metadataCacheItem, PrimitiveResourceTypeMap primitiveResourceTypeMap) { Debug.Assert(resourceTypeCacheItem != null, "resourceTypeCacheItem != null"); Debug.Assert(workspace != null, "workspace != null"); var resourceType = resourceTypeCacheItem.ResourceType; // Find the type from the OSpace IProviderType edmType = workspace.GetProviderType(resourceType.FullName); foreach (IProviderMember member in edmType.Members) { ResourcePropertyKind kind = (ResourcePropertyKind)(-1); // ObjectContextServiceProvider fails with NullReferenceException when an entity property is not public. // If the property on the CLR type which is representing the EDM type has non-public properties but those properties are part of the // conceptual model, the server will try to load CLR metadata for these properties. // The Type.GetProperty(propertyName) method used BindingFlags.Instance | BindingFlags.Public by default if no binding flags are specified. // Since the property was not found with these binding flags, the GetProperty method returns null, which we didn't check for in v1 and v2 and threw an NRE. // We now check for null return values from this function and throw if we find that the model property declared on the CLR type is not public. PropertyInfo propertyInfo = resourceType.InstanceType.GetProperty(member.Name, BindingFlags.Instance | BindingFlags.Public); if (propertyInfo == null) { throw new DataServiceException((int)HttpStatusCode.InternalServerError, Strings.ObjectContext_PublicPropertyNotDefinedOnType(edmType.Name, member.Name)); } ResourceType propertyType = null; switch (member.EdmTypeKind) { case BuiltInTypeKind.PrimitiveType: #if !INTERNAL_DROP && !EFRTM Type propertyClrType = ObjectContextSpatialUtil.IsDbGeography(propertyInfo.PropertyType) ? typeof(Geography) : propertyInfo.PropertyType; #else Type propertyClrType = propertyInfo.PropertyType; #endif propertyType = primitiveResourceTypeMap.GetPrimitive(propertyClrType); if (propertyType == null) { throw new NotSupportedException(Strings.ObjectContext_PrimitiveTypeNotSupported(member.Name, edmType.Name, member.EdmTypeName)); } if (member.IsKey) { kind = ResourcePropertyKind.Key | ResourcePropertyKind.Primitive; } else { kind = ResourcePropertyKind.Primitive; } break; case BuiltInTypeKind.ComplexType: kind = ResourcePropertyKind.ComplexType; propertyType = metadataCacheItem.TryGetResourceType(propertyInfo.PropertyType); break; case BuiltInTypeKind.EntityType: kind = ResourcePropertyKind.ResourceReference; propertyType = metadataCacheItem.TryGetResourceType(propertyInfo.PropertyType); break; case BuiltInTypeKind.CollectionType: kind = ResourcePropertyKind.ResourceSetReference; Type collectionItemClrType = workspace.GetClrType(member.CollectionItemType); Debug.Assert(!WebUtil.IsPrimitiveType(collectionItemClrType), "We don't support collections of primitives, we shouldn't see one here"); propertyType = metadataCacheItem.TryGetResourceType(collectionItemClrType); break; default: throw new NotSupportedException(Strings.ObjectContext_PrimitiveTypeNotSupported(member.Name, edmType.Name, member.EdmTypeName)); } Debug.Assert(propertyType != null, "propertyType != null"); ResourceProperty resourceProperty = new ResourceProperty(propertyInfo.Name, kind, propertyType); SetMimeTypeForMappedMember(resourceProperty, member); resourceType.AddProperty(resourceProperty); #if !EF6Provider ObjectContextServiceProvider.PopulateFacets(resourceProperty, member.Facets, resourceProperty.ResourceType.ResourceTypeKind == ResourceTypeKind.EntityType /*ignoreNullableAnnotation*/); ObjectContextServiceProvider.PopulateAnnotations(member.MetadataProperties, resourceProperty.AddCustomAnnotation); #endif resourceTypeCacheItem.AddResourcePropertyCacheItem(resourceProperty, new ObjectContextResourcePropertyCacheItem(propertyInfo, member)); } }
/// <summary> /// Populates metadata from the given object context /// </summary> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem in which metadata needs to be populated.</param> protected sealed override void PopulateMetadata(ProviderMetadataCacheItem metadataCacheItem) { Debug.Assert(metadataCacheItem != null, "metadataCacheItem != null"); Debug.Assert(this.ObjectContext != null, "this.ObjectContext != null"); InitializeObjectItemCollection(this.ObjectContext, this.DataSourceType.Assembly); MetadataWorkspace metadataWorkspace = this.ObjectContext.MetadataWorkspace; // Create Resource types for all the top level entity types and complexTypes foreach (StructuralType edmType in metadataWorkspace.GetItems<StructuralType>(DataSpace.CSpace)) { if (edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType || edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType) { // Populates metadata for the given types and all its base types if (PopulateTypeMetadata(metadataWorkspace, edmType, metadataCacheItem) == null) { this.typesWithoutOSpaceMetadata.Add(edmType); } } } foreach (EntityContainer entityContainer in metadataWorkspace.GetItems<EntityContainer>(DataSpace.CSpace)) { bool defaultEntityContainer = entityContainer.Name == this.ObjectContext.DefaultContainerName; // Get the list of entity sets (Ignore the relationship sets, since we won't allow that to be queried directly foreach (EntitySetBase entitySetBase in entityContainer.BaseEntitySets) { // Ignore all the association sets for the type being, since we are caching only entity sets if (entitySetBase.BuiltInTypeKind != BuiltInTypeKind.EntitySet) { continue; } EntitySet entitySet = (EntitySet)entitySetBase; Type elementType = GetClrTypeForCSpaceType(metadataWorkspace, entitySet.ElementType); ResourceType resourceType; if (elementType == null || (resourceType = metadataCacheItem.TryGetResourceType(elementType)) == null) { throw new InvalidOperationException(Strings.ObjectContextServiceProvider_OSpaceTypeNotFound(entitySet.ElementType.FullName)); } string entitySetName = GetEntitySetName(entitySet.Name, entitySet.EntityContainer.Name, defaultEntityContainer); ResourceSet resourceSet = new ResourceSet(entitySetName, resourceType); metadataCacheItem.EntitySets.Add(entitySetName, resourceSet); #if !EF6Provider resourceSet.EntityContainerName = entityContainer.Name; ObjectContextServiceProvider.PopulateAnnotations(entitySetBase.MetadataProperties, resourceSet.AddCustomAnnotation); #endif metadataCacheItem.QueryRootCache.Add(resourceSet, this.BuildQueryRootDelegate(resourceSet)); } } // Now go and populate the member information for each resource type foreach (var resourceTypeCacheItem in metadataCacheItem.ResourceTypeCacheItems) { var resourceType = resourceTypeCacheItem.ResourceType; if (resourceType.ResourceTypeKind == ResourceTypeKind.Primitive) { continue; } PopulateMemberMetadata(resourceTypeCacheItem, new ObjectContextMetadata(metadataWorkspace), metadataCacheItem, PrimitiveResourceTypeMap.TypeMap); } }
/// <summary> /// Populates the metadata for the given type and its base type /// </summary> /// <param name="workspace">metadata workspace containing all the metadata information</param> /// <param name="edmType"> type whose metadata needs to be populated </param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <returns>returns the resource type corresponding to the given edmType</returns> private static ResourceType PopulateTypeMetadata( MetadataWorkspace workspace, StructuralType edmType, ProviderMetadataCacheItem metadataCacheItem) { Debug.Assert( edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType || edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType, "type must be entity or complex type"); ResourceType resourceType = null; Type clrType = GetClrTypeForCSpaceType(workspace, edmType); if (clrType != null) { resourceType = metadataCacheItem.TryGetResourceType(clrType); if (resourceType == null) { ResourceType baseType = null; if (edmType.BaseType != null) { baseType = PopulateTypeMetadata(workspace, (StructuralType)edmType.BaseType, metadataCacheItem); } resourceType = CreateResourceType(edmType, clrType, baseType, metadataCacheItem); } } return resourceType; }
/// <summary> /// Creates a new instance of resource type given the cspace structural type and mapping clr type. /// </summary> /// <param name="cspaceType">cspace structural type.</param> /// <param name="clrType">mapping clr type for the given structural type.</param> /// <param name="baseResourceType">the base resource type for the given resource type.</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <returns>the new resource type instance created for the given cspace type.</returns> private static ResourceType CreateResourceType( StructuralType cspaceType, Type clrType, ResourceType baseResourceType, ProviderMetadataCacheItem metadataCacheItem) { ResourceTypeKind resourceTypeKind = cspaceType.BuiltInTypeKind == BuiltInTypeKind.EntityType ? ResourceTypeKind.EntityType : ResourceTypeKind.ComplexType; // We do not support open types in Object Context provider yet. ResourceType resourceType = new ResourceType(clrType, resourceTypeKind, baseResourceType, cspaceType.NamespaceName, cspaceType.Name, clrType.IsAbstract); if (GetEntityTypeDefaultStreamProperty(cspaceType)) { resourceType.IsMediaLinkEntry = true; } // Add stream properties that are marked NamedStream in clrType to resourceType. AddStreamProperties(resourceType, clrType, baseResourceType == null); metadataCacheItem.AddResourceType(clrType, resourceType); var childTypes = metadataCacheItem.ChildTypesCache; childTypes.Add(resourceType, null); if (baseResourceType != null) { Debug.Assert(childTypes.ContainsKey(baseResourceType), "childTypes.ContainsKey(baseResourceType)"); if (childTypes[baseResourceType] == null) { childTypes[baseResourceType] = new List<ResourceType>(); } childTypes[baseResourceType].Add(resourceType); } #if !EF6Provider ObjectContextServiceProvider.PopulateAnnotations(cspaceType.MetadataProperties, resourceType.AddCustomAnnotation); #endif return resourceType; }
/// <summary> /// Populate metadata for the given clr type. /// </summary> /// <param name="type">type whose metadata needs to be loaded.</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <returns>resource type containing metadata for the given clr type.</returns> protected abstract ResourceType PopulateMetadataForType(Type type, ProviderMetadataCacheItem metadataCacheItem);
/// <summary> /// Populate types for metadata specified by the provider /// </summary> /// <param name="userSpecifiedTypes">list of types specified by the provider</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> protected sealed override void PopulateMetadataForUserSpecifiedTypes( IEnumerable<Type> userSpecifiedTypes, ProviderMetadataCacheItem metadataCacheItem) { Queue<ResourceType> unvisitedTypes = new Queue<ResourceType>(); foreach (Type type in userSpecifiedTypes) { ResourceType resourceType; if (TryGetType(metadataCacheItem, type, out resourceType)) { continue; } if (IsEntityOrComplexType(type, metadataCacheItem, unvisitedTypes) == null) { throw new InvalidOperationException(Strings.BadProvider_InvalidTypeSpecified(type.FullName)); } } PopulateMetadataForTypes(metadataCacheItem, unvisitedTypes); }
/// <summary> /// Find out all the derived types in the list of assemblies and then populate metadata for those types /// </summary> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <param name="unvisitedTypes">list of unvisited types</param> private static void PopulateMetadataForDerivedTypes( ProviderMetadataCacheItem metadataCacheItem, Queue<ResourceType> unvisitedTypes) { Debug.Assert(metadataCacheItem != null, "metadataCacheItem != null"); Debug.Assert(unvisitedTypes != null, "unvisitedTypes != null"); // Find all the root resource entity types List<ResourceType> rootTypes = new List<ResourceType>(); // To find the list of derived types, we should use the types // referred by the sets as the base type, to make sure we are // loading metadata only for types that can be referenced by // the given sets. foreach (ResourceSet resourceSet in metadataCacheItem.EntitySets.Values) { rootTypes.Add(resourceSet.ResourceType); } // Use the default comparer, which calls Assembly.Equals (not a simple reference comparison). HashSet<Assembly> assemblies = new HashSet<Assembly>(EqualityComparer<Assembly>.Default); List<Type> derivedTypes = new List<Type>(); // Walk through all the types in the assemblies and find all the derived types foreach (var resourceTypeCacheItem in metadataCacheItem.ResourceTypeCacheItems) { var resourceType = resourceTypeCacheItem.ResourceType; // No need to look into primitive types, as these live in system assemblies. if (resourceType.ResourceTypeKind == ResourceTypeKind.Primitive) { continue; } Assembly assembly = resourceType.InstanceType.Assembly; //// ignore if the assembly has already been scanned if (assemblies.Contains(assembly)) { continue; } // Walk all the types in that assembly foreach (Type type in assembly.GetTypes()) { // skip all the non visible types or types which have generic parameters if (!type.IsVisible || HasGenericParameters(type)) { continue; } // Skip the type if its already loaded if (metadataCacheItem.TryGetResourceType(type) != null) { continue; } // Check if this type dervies from any one of the root types for (int i = 0; i < rootTypes.Count; i++) { if (rootTypes[i].InstanceType.IsAssignableFrom(type)) { derivedTypes.Add(type); } } } assemblies.Add(assembly); } foreach (Type type in derivedTypes) { BuildHierarchyForEntityType(type, metadataCacheItem, unvisitedTypes, false /* entityTypeCandidate */); PopulateMetadataForTypes(metadataCacheItem, unvisitedTypes); } }
/// <summary> /// Populates the metadata for the given provider /// </summary> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem in which metadata needs to be populated.</param> protected abstract void PopulateMetadata(ProviderMetadataCacheItem metadataCacheItem);
private static void BuildTypeProperties( ResourceType parentResourceType, ProviderMetadataCacheItem metadataCacheItem, Queue<ResourceType> unvisitedTypes) { Debug.Assert(parentResourceType != null, "parentResourceType != null"); Debug.Assert(metadataCacheItem != null, "metadataCacheItem != null"); Debug.Assert(unvisitedTypes != null, "unvisitedTypes != null"); BindingFlags bindingFlags = WebUtil.PublicInstanceBindingFlags; // For non root types, we should only look for properties that are declared for this type if (parentResourceType.BaseType != null) { bindingFlags = bindingFlags | BindingFlags.DeclaredOnly; } HashSet<string> propertiesToBeIgnored = new HashSet<string>(IgnorePropertiesAttribute.GetProperties(parentResourceType.InstanceType, false /*inherit*/, bindingFlags), StringComparer.Ordinal); Debug.Assert(parentResourceType.IsOpenType == false, "ReflectionServiceProvider does not support Open types."); HashSet<string> etagPropertyNames = new HashSet<string>(LoadETagProperties(parentResourceType), StringComparer.Ordinal); ResourceKeyKind keyKind = (ResourceKeyKind)Int32.MaxValue; PropertyInfo[] properties = parentResourceType.InstanceType.GetProperties(bindingFlags); // Should not allow System.Object on server // The general fix for this bug is to not support any resource type that doesn't have // any publically visible properties, including System.object and also custom types which don't have any properties. if (!properties.Any() && parentResourceType.BaseType == null) { throw new NotSupportedException(Strings.ReflectionProvider_ResourceTypeHasNoPublicallyVisibleProperties(parentResourceType.FullName)); } foreach (PropertyInfo property in properties) { // Ignore the properties which are specified in the IgnoreProperties attribute if (propertiesToBeIgnored.Contains(property.Name)) { continue; } if (property.CanRead && property.GetIndexParameters().Length == 0) { ResourcePropertyKind kind = (ResourcePropertyKind)(-1); ResourceType resourceType; Type resourcePropertyType = property.PropertyType; bool collection = false; if (!TryGetType(metadataCacheItem, resourcePropertyType, out resourceType)) { Type collectionType = GetIEnumerableElement(property.PropertyType); if (collectionType != null) { TryGetType(metadataCacheItem, collectionType, out resourceType); // Even if the above method returns false, we should set the // following variable appropriately, so that we can use them below collection = true; resourcePropertyType = collectionType; } } if (resourceType != null) { #region Already Known Type if (resourceType.ResourceTypeKind == ResourceTypeKind.Primitive) { if (collection) { // If it's a collection it can't be a key property (we don't allow collection properties as key) kind = ResourcePropertyKind.Collection; } else { ResourceKeyKind currentKeyKind; if (parentResourceType.BaseType == null && parentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType && IsPropertyKeyProperty(property, out currentKeyKind)) { // Check for key property only on root types, since keys must be defined on the root types if ((int)currentKeyKind < (int)keyKind) { if (parentResourceType.KeyProperties.Count != 0) { // Remove the existing property as key property - mark it as non key property parentResourceType.RemoveKeyProperties(); } keyKind = currentKeyKind; kind = ResourcePropertyKind.Key | ResourcePropertyKind.Primitive; } else if ((int)currentKeyKind == (int)keyKind) { Debug.Assert(currentKeyKind == ResourceKeyKind.AttributedKey, "This is the only way of specifying composite keys"); kind = ResourcePropertyKind.Key | ResourcePropertyKind.Primitive; } else { kind = ResourcePropertyKind.Primitive; } } else { kind = ResourcePropertyKind.Primitive; } } } else if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) { kind = collection ? ResourcePropertyKind.Collection : ResourcePropertyKind.ComplexType; } else if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) { kind = collection ? ResourcePropertyKind.ResourceSetReference : ResourcePropertyKind.ResourceReference; } #endregion // Already Known Type } else { resourceType = IsEntityOrComplexType(resourcePropertyType, metadataCacheItem, unvisitedTypes); if (resourceType != null) { if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) { if (collection) { // For backward compat reasons only look closely on the item type if the property is a collection (IEnumerable<T>) // and the T looks like a complex type. // In that case we used to only allow entity types as T, now with collection properties we also allow primitive and complex types, // but we must explicitely disallow collection of collection, that is any T which implements IEnumerable<T>. // Note that we can't just make IEnumerable<T> not be a complex types since in certain cases we used to recognize it as such // and we would break those cases. One example is user registered known types. Type collectionType = GetIEnumerableElement(resourcePropertyType); if (collectionType != null) { throw new InvalidOperationException(Strings.ReflectionProvider_CollectionOfCollectionProperty(property.Name, parentResourceType.FullName)); } kind = ResourcePropertyKind.Collection; } else { kind = ResourcePropertyKind.ComplexType; } } else { Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "Must be an entity type"); kind = collection ? ResourcePropertyKind.ResourceSetReference : ResourcePropertyKind.ResourceReference; } } } // if resource type is null OR // if complex type has a property of entity type if (resourceType == null || (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType && parentResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)) { if (resourceType == null) { // Provide a better error message for collection of collection if (collection && GetIEnumerableElement(resourcePropertyType) != null) { throw new InvalidOperationException(Strings.ReflectionProvider_CollectionOfCollectionProperty(property.Name, parentResourceType.FullName)); } // Provide a better error message for collection of wrong types if (collection) { throw new InvalidOperationException(Strings.ReflectionProvider_CollectionOfUnsupportedTypeProperty(property.Name, parentResourceType.FullName, resourcePropertyType)); } if (CommonUtil.IsUnsupportedType(resourcePropertyType)) { throw new InvalidOperationException(Strings.BadProvider_UnsupportedPropertyType(property.Name, parentResourceType.FullName)); } throw new InvalidOperationException(Strings.ReflectionProvider_InvalidProperty(property.Name, parentResourceType.FullName)); } // Navigation property on a complex type is not supported throw new InvalidOperationException(Strings.ReflectionProvider_ComplexTypeWithNavigationProperty(property.Name, parentResourceType.FullName)); } if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) { ResourceSet container = InternalGetContainerForResourceType(resourcePropertyType, metadataCacheItem.EntitySets.Values); if (container == null) { throw new InvalidOperationException(Strings.ReflectionProvider_EntityPropertyWithNoEntitySet(parentResourceType.FullName, property.Name)); } } if (kind == ResourcePropertyKind.Collection) { // Collection properties need the collection type (representing the IEnumerable<> part). resourceType = ResourceType.GetCollectionResourceType(resourceType); } if (etagPropertyNames.Remove(property.Name)) { kind |= ResourcePropertyKind.ETag; } ResourceProperty resourceProperty = new ResourceProperty(property.Name, kind, resourceType); MimeTypeAttribute attribute = BaseServiceProvider.GetMimeTypeAttribute(property); if (attribute != null) { resourceProperty.MimeType = attribute.MimeType; } parentResourceType.AddProperty(resourceProperty); } else { throw new InvalidOperationException(Strings.ReflectionProvider_InvalidProperty(property.Name, parentResourceType.FullName)); } } if (parentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType && (parentResourceType.KeyProperties == null || parentResourceType.KeyProperties.Count == 0)) { throw new InvalidOperationException(Strings.ReflectionProvider_KeyPropertiesCannotBeIgnored(parentResourceType.FullName)); } if (etagPropertyNames.Count != 0) { throw new InvalidOperationException(Strings.ReflectionProvider_ETagPropertyNameNotValid(etagPropertyNames.ElementAt(0), parentResourceType.FullName)); } }
/// <summary> /// If the given type is a entity or complex type, it returns the resource type corresponding to the given type /// </summary> /// <param name="type">clr type</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <param name="unvisitedTypes">list of unvisited types</param> /// <returns>resource type corresponding to the given clr type, if the clr type is entity or complex</returns> private static ResourceType IsEntityOrComplexType( Type type, ProviderMetadataCacheItem metadataCacheItem, Queue<ResourceType> unvisitedTypes) { // Ignore values types here. We do not support resources of values type (entity or complex) if (type.IsValueType || CommonUtil.IsUnsupportedType(type)) { return null; } ResourceType resourceType = BuildHierarchyForEntityType(type, metadataCacheItem, unvisitedTypes, false /* entityTypeCandidate */); if (resourceType == null && IsComplexType(type)) { resourceType = ReflectionServiceProvider.CreateResourceType(type, ResourceTypeKind.ComplexType, null, metadataCacheItem); unvisitedTypes.Enqueue(resourceType); } return resourceType; }
/// <summary> /// Walks through the list of ancestors and finds the root base type and collects metadata for the entire chain of ancestors /// </summary> /// <param name="type">type whose ancestors metadata needs to be populated</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <param name="unvisitedTypes">list of unvisited types</param> /// <param name="entityTypeCandidate">Whether <paramref name="type"/> is a candidate to be an entity type.</param> /// <returns>return true if this given type is a entity type, otherwise returns false</returns> private static ResourceType BuildHierarchyForEntityType( Type type, ProviderMetadataCacheItem metadataCacheItem, Queue<ResourceType> unvisitedTypes, bool entityTypeCandidate) { List<Type> ancestors = new List<Type>(); if (!type.IsVisible) { return null; } if (CommonUtil.IsUnsupportedType(type)) { // deriving from an unsupported type is not allowed throw new InvalidOperationException(Strings.BadProvider_UnsupportedType(type.FullName)); } Type baseType = type; ResourceType baseResourceType = null; // Since this method is also used on property types, which can be interfaces, // Base types can be null while (baseType != null) { // Try and check if the base type is already loaded if (TryGetType(metadataCacheItem, baseType, out baseResourceType)) { break; } ancestors.Add(baseType); baseType = baseType.BaseType; } if (baseResourceType == null) { // If entityTypeCandidate is false, then it means that the current type can't // be a entity type with keys. In other words, it must derive from an existing // type. Otherwise, its not an entity type if (entityTypeCandidate == false) { return null; } // Find the last ancestor which has key defined for (int i = ancestors.Count - 1; i >= 0; i--) { if (CommonUtil.IsUnsupportedType(ancestors[i])) { // deriving from an unsupported type is not allowed throw new InvalidOperationException(Strings.BadProvider_UnsupportedAncestorType(type.FullName, ancestors[i].FullName)); } if (DoesTypeHaveKeyProperties(ancestors[i], entityTypeCandidate)) { break; } // Else this type is not interesting. Remove it from the ancestors list ancestors.RemoveAt(i); } } else if (baseResourceType.ResourceTypeKind != ResourceTypeKind.EntityType) { return null; } else if (ancestors.Count == 0) { // we might have found the top level element.So just return return baseResourceType; } // For all the valid ancestors, add the type to the list of types encountered // and unvisited types // its important that we enqueue the ancestors first, since when we populate member metadata // we can make sure that the base type is fully populated for (int i = ancestors.Count - 1; i >= 0; i--) { ResourceType entityType = ReflectionServiceProvider.CreateResourceType(ancestors[i], ResourceTypeKind.EntityType, baseResourceType, metadataCacheItem); unvisitedTypes.Enqueue(entityType); baseResourceType = entityType; } return baseResourceType; }
/// <summary> /// Populates the metadata for the given unvisited types and all the associated types with this type /// </summary> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <param name="unvisitedTypes">list of unvisited type</param> private static void PopulateMetadataForTypes( ProviderMetadataCacheItem metadataCacheItem, Queue<ResourceType> unvisitedTypes) { Debug.Assert(metadataCacheItem != null, "metadataCacheItem != null"); Debug.Assert(unvisitedTypes != null, "unvisitedTypes != null"); // Start walking down all the types while (unvisitedTypes.Count != 0) { // get the unvisited element ResourceType type = unvisitedTypes.Dequeue(); // Go through all the properties and find out one or more complex types BuildTypeProperties(type, metadataCacheItem, unvisitedTypes); } }
/// <summary> /// Populate metadata for the given clr type. /// </summary> /// <param name="type">type whose metadata needs to be loaded.</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <returns>resource type containing metadata for the given clr type.</returns> protected sealed override ResourceType PopulateMetadataForType( Type type, ProviderMetadataCacheItem metadataCacheItem) { Queue<ResourceType> unvisitedTypes = new Queue<ResourceType>(); ResourceType resourceType; if (!TryGetType(metadataCacheItem, type, out resourceType)) { resourceType = IsEntityOrComplexType(type, metadataCacheItem, unvisitedTypes); if (resourceType != null) { PopulateMetadataForTypes(metadataCacheItem, unvisitedTypes); } } return resourceType; }
/// <summary> /// Looks up the metadata in the cache. If not present in the cache, then loads metadata from the provider. /// </summary> /// <param name="skipServiceOperations">Should service operations be loaded.</param> internal void LoadMetadata(bool skipServiceOperations) { Type dataServiceType = this.dataServiceInstance.GetType(); Type dataSourceType = this.dataSourceInstance.GetType(); // If 2 threads enter at the same time, and none of them find the metadata (since its the first time), // both of them will load new metadata and try to add them to the cache. But the cache is thread safe, hence // before adding, it will check again and return if there is an existing metadata. If it is, we should discard the // metadata that just got initialized and use one from the cache. this.metadata = MetadataCache<ProviderMetadataCacheItem>.TryLookup(dataServiceType, this.dataSourceInstance); if (this.metadata == null) { this.metadata = new ProviderMetadataCacheItem(dataSourceType); // Populate metadata in provider. this.PopulateMetadata(this.metadata); // Populate service operations only on-demand. if (!skipServiceOperations) { this.LoadServiceOperations(); } this.metadataRequiresInitialization = true; // no need to add metadata yet in the cache, since there might be some encountered while applying // configuration or while making it read-only. } }
/// <summary> /// returns the new resource type instance /// </summary> /// <param name="type">backing clr type for the resource.</param> /// <param name="kind">kind of the resource.</param> /// <param name="baseType">base type of the resource.</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <returns>returns a new instance of the resource type containing all the metadata.</returns> private static ResourceType CreateResourceType( Type type, ResourceTypeKind kind, ResourceType baseType, ProviderMetadataCacheItem metadataCacheItem) { ResourceType resourceType = new ResourceType(type, kind, baseType, type.Namespace, CommonUtil.GetModelTypeName(type), type.IsAbstract); resourceType.IsOpenType = false; // We need to look at inherited attributes as well so we pass true for inherit argument. if (type.GetCustomAttributes(typeof(HasStreamAttribute), true /* inherit */).Length == 1) { resourceType.IsMediaLinkEntry = true; } // Add stream properties that are marked NamedStream in clrType to resourceType. AddStreamProperties(resourceType, type, baseType == null); metadataCacheItem.AddResourceType(type, resourceType); var childTypes = metadataCacheItem.ChildTypesCache; childTypes.Add(resourceType, null); if (baseType != null) { Debug.Assert(childTypes.ContainsKey(baseType), "childTypes.ContainsKey(baseType)"); if (childTypes[baseType] == null) { childTypes[baseType] = new List<ResourceType>(); } childTypes[baseType].Add(resourceType); } return resourceType; }
/// <summary> /// Find the corresponding ResourceType for a given Type, primitive or not /// </summary> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <param name="type">Type to look for</param> /// <param name="resourceType">Corresponding ResourceType, if found</param> /// <returns>True if type found, false otherwise</returns> protected static bool TryGetType(ProviderMetadataCacheItem metadataCacheItem, Type type, out ResourceType resourceType) { Debug.Assert(metadataCacheItem != null, "metadataCacheItem != null"); Debug.Assert(type != null, "type != null"); resourceType = PrimitiveResourceTypeMap.TypeMap.GetPrimitive(type); if (resourceType == null) { resourceType = metadataCacheItem.TryGetResourceType(type); } return resourceType != null; }
/// <summary> /// Populate types for metadata specified by the provider /// </summary> /// <param name="userSpecifiedTypes">list of types specified by the provider</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> protected sealed override void PopulateMetadataForUserSpecifiedTypes( IEnumerable<Type> userSpecifiedTypes, ProviderMetadataCacheItem metadataCacheItem) { foreach (Type type in userSpecifiedTypes) { if (this.PopulateMetadataForType(type, metadataCacheItem) == null) { throw new InvalidOperationException(Strings.BadProvider_InvalidTypeSpecified(type.FullName)); } } // If there is a type in the model, for which we couldn't load the metadata, we should throw. if (this.typesWithoutOSpaceMetadata.Count != 0) { throw new InvalidOperationException(Strings.ObjectContext_UnableToLoadMetadataForType(this.typesWithoutOSpaceMetadata[0].FullName)); } this.typesWithoutOSpaceMetadata = null; }
/// <summary> /// Populate types for metadata specified by the provider /// </summary> /// <param name="userSpecifiedTypes">list of types specified by the provider</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> protected abstract void PopulateMetadataForUserSpecifiedTypes(IEnumerable<Type> userSpecifiedTypes, ProviderMetadataCacheItem metadataCacheItem);
/// <summary> /// Populate metadata for the given clr type. /// </summary> /// <param name="type">type whose metadata needs to be loaded.</param> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param> /// <returns>resource type containing metadata for the given clr type.</returns> protected sealed override ResourceType PopulateMetadataForType(Type type, ProviderMetadataCacheItem metadataCacheItem) { Debug.Assert(!WebUtil.IsPrimitiveType(type), "Why are we trying to load metadata for a primitive type?"); ResourceType resourceType = metadataCacheItem.TryGetResourceType(type); if (resourceType == null) { InitializeObjectItemCollection(this.ObjectContext, type.Assembly); ObjectItemCollection objectItemCollection = (ObjectItemCollection)this.ObjectContext.MetadataWorkspace.GetItemCollection(DataSpace.OSpace); StructuralType ospaceType, cspaceType; if (objectItemCollection.TryGetItem<StructuralType>(type.FullName, out ospaceType)) { if (this.ObjectContext.MetadataWorkspace.TryGetEdmSpaceType(ospaceType, out cspaceType)) { ResourceType baseType = null; if (cspaceType.BaseType != null) { baseType = this.PopulateMetadataForType(type.BaseType, metadataCacheItem); } resourceType = CreateResourceType(cspaceType, type, baseType, metadataCacheItem); this.typesWithoutOSpaceMetadata.Remove(cspaceType); } } } return resourceType; }
/// <summary>Make all the metadata readonly</summary> private void MakeMetadataReadonly() { Debug.Assert(this.metadataRequiresInitialization, "Should only call when initializing metadata."); foreach (ResourceSet container in this.ResourceSets) { container.SetReadOnly(); } foreach (ResourceType resourceType in this.Types) { resourceType.SetReadOnly(); // This will cause Properties collection to be initialized and validated. resourceType.PropertiesDeclaredOnThisType.Count(); } foreach (ServiceOperation operation in this.ServiceOperations) { operation.SetReadOnly(); } // After metadata has been completely loaded, add it to the cache. this.metadata = MetadataCache<ProviderMetadataCacheItem>.AddCacheItem(this.dataServiceInstance.GetType(), this.dataSourceInstance, this.metadata); }
/// <summary>Populates the metadata for this provider.</summary> /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem in which metadata needs to be populated.</param> protected sealed override void PopulateMetadata(ProviderMetadataCacheItem metadataCacheItem) { Queue<ResourceType> unvisitedTypes = new Queue<ResourceType>(); // Get the list of properties to be ignored. List<string> propertiesToBeIgnored = new List<string>( IgnorePropertiesAttribute.GetProperties(this.DataSourceType, true /*inherit*/, WebUtil.PublicInstanceBindingFlags)); PropertyInfo[] properties = this.DataSourceType.GetProperties(WebUtil.PublicInstanceBindingFlags); foreach (PropertyInfo property in properties) { if (!propertiesToBeIgnored.Contains(property.Name) && property.CanRead && property.GetIndexParameters().Length == 0) { Type elementType = BaseServiceProvider.GetIQueryableElement(property.PropertyType); if (elementType != null) { // If the element type has key defined (in itself or one of its ancestors) ResourceType resourceType = BuildHierarchyForEntityType(elementType, metadataCacheItem, unvisitedTypes, true /* entity type candidate */); if (resourceType != null) { // We do not allow MEST scenario for reflection provider foreach (KeyValuePair<string, ResourceSet> entitySetInfo in metadataCacheItem.EntitySets) { Type entitySetType = entitySetInfo.Value.ResourceType.InstanceType; if (entitySetType.IsAssignableFrom(elementType)) { throw new InvalidOperationException(Strings.ReflectionProvider_MultipleEntitySetsForSameType(entitySetInfo.Value.Name, property.Name, entitySetType.FullName, resourceType.FullName)); } if (elementType.IsAssignableFrom(entitySetType)) { throw new InvalidOperationException(Strings.ReflectionProvider_MultipleEntitySetsForSameType(property.Name, entitySetInfo.Value.Name, resourceType.FullName, entitySetType.FullName)); } } // Add the entity set to the list of entity sets. ResourceSet resourceContainer = new ResourceSet(property.Name, resourceType); metadataCacheItem.EntitySets.Add(property.Name, resourceContainer); metadataCacheItem.QueryRootCache.Add(resourceContainer, this.BuildQueryRootDelegate(resourceContainer)); } else { throw new InvalidOperationException(Strings.ReflectionProvider_InvalidEntitySetProperty(property.Name, XmlConvert.EncodeName(((IDataServiceMetadataProvider)this).ContainerName))); } } } } // Populate the metadata for all the types in unvisited types // and also their properties and populates metadata about property types PopulateMetadataForTypes(metadataCacheItem, unvisitedTypes); // At this point, we should have all the top level entity types and the complex types PopulateMetadataForDerivedTypes(metadataCacheItem, unvisitedTypes); }