예제 #1
0
        /// <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>
        /// 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 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 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>
        /// 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>
        /// 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);
            }
        }