ObjectItemAttributeAssemblyLoader.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Metadata / ObjectLayer / ObjectItemAttributeAssemblyLoader.cs / 1305376 / ObjectItemAttributeAssemblyLoader.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

 
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Objects.DataClasses;
using System.Diagnostics; 
using System.Reflection;
 
namespace System.Data.Metadata.Edm 
{
    ///  
    /// Class for representing a collection of items for the object layer.
    /// Most of the implemetation for actual maintainance of the collection is
    /// done by ItemCollection
    ///  
    internal sealed class ObjectItemAttributeAssemblyLoader : ObjectItemAssemblyLoader
    { 
        #region Fields 

        // list of unresolved navigation properties 
        private readonly List _unresolvedNavigationProperties = new List();
        private new MutableAssemblyCacheEntry CacheEntry { get { return (MutableAssemblyCacheEntry)base.CacheEntry; } }
        private List _referenceResolutions = new List();
 
        #endregion
 
        #region Constructor 
        internal ObjectItemAttributeAssemblyLoader(Assembly assembly, ObjectItemLoadingSessionData sessionData)
            :base(assembly, new MutableAssemblyCacheEntry(), sessionData) 
        {
            Debug.Assert(Create == sessionData.ObjectItemAssemblyLoaderFactory, "Why is there a different factory creating this class");

        } 
        #endregion
 
        #region Methods 

        internal override void OnLevel1SessionProcessing() 
        {
            foreach (Action resolve in _referenceResolutions)
            {
                resolve(); 
            }
        } 
 
        internal override void OnLevel2SessionProcessing()
        { 
            foreach (Action resolve in _unresolvedNavigationProperties)
            {
                resolve();
            } 
        }
        ///  
        /// Loads the given assembly and all the other referencd assemblies in the cache. If the assembly was already present 
        /// then it loads from the cache
        ///  
        /// 
        /// true if the assembly was already loaded in the cache
        internal override void Load()
        { 
            Debug.Assert(IsSchemaAttributePresent(SourceAssembly), "LoadAssembly shouldn't be called with assembly having no schema attribute");
            Debug.Assert(!SessionData.KnownAssemblies.Contains(SourceAssembly, SessionData.ObjectItemAssemblyLoaderFactory, SessionData.EdmItemCollection), "InternalLoadAssemblyFromCache: This assembly must not be present in the list of known assemblies"); 
 
            base.Load();
        } 

        protected override void AddToAssembliesLoaded()
        {
            SessionData.AssembliesLoaded.Add(SourceAssembly, CacheEntry); 
        }
        ///  
        /// Check to see if the type is already loaded - either in the typesInLoading, or ObjectItemCollection or 
        /// in the global cache
        ///  
        /// 
        /// 
        /// 
        private bool TryGetLoadedType(Type clrType, out EdmType edmType) 
        {
            if (SessionData.TypesInLoading.TryGetValue(clrType.FullName, out edmType) || 
                TryGetCachedEdmType(clrType, out edmType)) 
            {
                // Check to make sure the CLR type we got is the same as the given one 
                if (edmType.ClrType != clrType)
                {
                    SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NewTypeConflictsWithExistingType(
                                                clrType.AssemblyQualifiedName, edmType.ClrType.AssemblyQualifiedName), edmType)); 
                    edmType = null;
                    return false; 
                } 
                return true;
            } 


            // Let's check to see if this type is a ref type, a nullable type, or a collection type, these are the types that
            // we need to take special care of them 
            if (clrType.IsGenericType)
            { 
                Type genericType = clrType.GetGenericTypeDefinition(); 

                // Try to resolve the element type into a type object 
                EdmType elementType;
                if (!TryGetLoadedType(clrType.GetGenericArguments()[0], out elementType))
                    return false;
 
                if (typeof(System.Collections.IEnumerable).IsAssignableFrom(clrType))
                { 
                    EntityType entityType = elementType as EntityType; 
                    if (entityType == null)
                    { 
                        // return null and let the caller deal with the error handling
                        return false;
                    }
                    edmType = entityType.GetCollectionType(); 
                }
                else 
                { 
                    edmType = elementType;
                } 

                return true;
            }
 

            edmType = null; 
            return false; 
        }
 
        private bool TryGetCachedEdmType(Type clrType, out EdmType edmType)
        {
            Debug.Assert(!SessionData.TypesInLoading.ContainsKey(clrType.FullName), "This should be called only after looking in typesInLoading");
            Debug.Assert(SessionData.EdmItemErrors.Count > 0 || // had an error during loading 
                        clrType.GetCustomAttributes(typeof(EdmTypeAttribute), false /*inherit*/).Length == 0 || // not a type we track
                        SourceAssembly != clrType.Assembly, // not from this assembly 
                        "Given that we don't have any error, if the type is part of this assembly, it should not be loaded from the cache"); 

            ImmutableAssemblyCacheEntry immutableCacheEntry; 
            if (SessionData.LockedAssemblyCache.TryGetValue(clrType.Assembly, out immutableCacheEntry))
            {
                Debug.Assert(SessionData.KnownAssemblies.Contains(clrType.Assembly, SessionData.LoaderCookie, SessionData.EdmItemCollection), "We should only be loading things directly from the cache if they are already in the collection");
                return immutableCacheEntry.TryGetEdmType(clrType.FullName, out edmType); 
            }
 
            edmType = null; 
            return false;
        } 

        #endregion
        /// 
        /// Loads the set of types from the given assembly and adds it to the given list of types 
        /// 
        /// context containing information for loading 
        protected override void LoadTypesFromAssembly() 
        {
            Debug.Assert(CacheEntry.TypesInAssembly.Count == 0); 

            LoadRelationshipTypes();

            // Loop through each type in the assembly and process it 
            foreach (Type type in SourceAssembly.GetTypes())
            { 
                // If the type doesn't have the same EdmTypeAttribute defined, then it's not a special type 
                // that we care about, skip it.
                if (!type.IsDefined(typeof(EdmTypeAttribute), false)) 
                {
                    continue;
                }
 
                // Load the metadata for this type
                LoadType(type); 
            } 

            if (_referenceResolutions.Count != 0) 
            {
                SessionData.RegisterForLevel1PostSessionProcessing(this);
            }
 
            if (_unresolvedNavigationProperties.Count != 0)
            { 
                SessionData.RegisterForLevel2PostSessionProcessing(this); 
            }
        } 

        /// 
        /// This method loads all the relationship type that this entity takes part in
        ///  
        /// 
        ///  
        private void LoadRelationshipTypes() 
        {
            foreach (EdmRelationshipAttribute roleAttribute in SourceAssembly.GetCustomAttributes(typeof(EdmRelationshipAttribute), false /*inherit*/)) 
            {
                // Check if there is an entry already with this name
                if (TryFindNullParametersInRelationshipAttribute(roleAttribute))
                { 
                    // don't give more errors for these same bad parameters
                    continue; 
                } 

                bool errorEncountered = false; 

                // return error if the role names are the same
                if (roleAttribute.Role1Name == roleAttribute.Role2Name)
                { 
                    SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.SameRoleNameOnRelationshipAttribute(roleAttribute.RelationshipName, roleAttribute.Role2Name),
                               null)); 
                    errorEncountered = true; 
                }
 

                if (!errorEncountered)
                {
                    AssociationType associationType = new AssociationType(roleAttribute.RelationshipName, roleAttribute.RelationshipNamespaceName, roleAttribute.IsForeignKey, DataSpace.OSpace); 
                    SessionData.TypesInLoading.Add(associationType.FullName, associationType);
                    TrackClosure(roleAttribute.Role1Type); 
                    TrackClosure(roleAttribute.Role2Type); 

                    // prevent lifting of loop vars 
                    string r1Name = roleAttribute.Role1Name;
                    Type r1Type = roleAttribute.Role1Type;
                    RelationshipMultiplicity r1Multiplicity = roleAttribute.Role1Multiplicity;
                    AddTypeResolver(() => 
                        ResolveAssociationEnd(associationType, r1Name, r1Type, r1Multiplicity));
 
                    // prevent lifting of loop vars 
                    string r2Name = roleAttribute.Role2Name;
                    Type r2Type = roleAttribute.Role2Type; 
                    RelationshipMultiplicity r2Multiplicity = roleAttribute.Role2Multiplicity;
                    AddTypeResolver(() =>
                        ResolveAssociationEnd(associationType, r2Name, r2Type, r2Multiplicity));
 
                    // get assembly entry and add association type to the list of types in the assembly
                    Debug.Assert(!CacheEntry.ContainsType(associationType.FullName), "Relationship type must not be present in the list of types"); 
                    CacheEntry.TypesInAssembly.Add(associationType); 
                }
            } 
        }

        private void ResolveAssociationEnd(AssociationType associationType, string roleName, Type clrType, RelationshipMultiplicity multiplicity)
        { 
            EntityType entityType;
            if (!TryGetRelationshipEndEntityType(clrType, out entityType)) 
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(associationType.Name, roleName, clrType),
                           null)); 
                return;
            }
            associationType.AddKeyMember(new AssociationEndMember(roleName, entityType.GetReferenceType(), multiplicity));
        } 
        /// 
        /// Load metadata of the given type - when you call this method, you should check and make sure that the type has 
        /// edm attribute. If it doesn't,we won't load the type and it will be returned as null 
        /// 
        ///  
        /// 
        /// 
        private void LoadType(Type clrType)
        { 
            Debug.Assert(clrType.Assembly == SourceAssembly, "Why are we loading a type that is not in our assembly?");
            Debug.Assert(!SessionData.TypesInLoading.ContainsKey(clrType.FullName), "Trying to load a type that is already loaded???"); 
 
            EdmType edmType = null;
 
            EdmTypeAttribute[] typeAttributes = (EdmTypeAttribute[])clrType.GetCustomAttributes(typeof(EdmTypeAttribute), false /*inherit*/);

            // the CLR doesn't allow types to have duplicate/multiple attribute declarations
 
            if (typeAttributes.Length != 0)
            { 
                if (clrType.IsNested) 
                {
                    SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NestedClassNotSupported(clrType.FullName, clrType.Assembly.FullName), null)); 
                    return;
                }
                EdmTypeAttribute typeAttribute = typeAttributes[0];
                string cspaceTypeName = String.IsNullOrEmpty(typeAttribute.Name) ? clrType.Name : typeAttribute.Name; 
                if (String.IsNullOrEmpty(typeAttribute.NamespaceName) && clrType.Namespace == null)
                { 
                    SessionData.EdmItemErrors.Add(new EdmItemError(Strings.Validator_TypeHasNoNamespace, edmType)); 
                    return;
                } 

                string cspaceNamespaceName = String.IsNullOrEmpty(typeAttribute.NamespaceName) ? clrType.Namespace : typeAttribute.NamespaceName;

                if (typeAttribute.GetType() == typeof(EdmEntityTypeAttribute)) 
                {
                    edmType = new ClrEntityType(clrType, cspaceNamespaceName, cspaceTypeName); 
                } 
                else
                { 
                    Debug.Assert(typeAttribute.GetType() == typeof(EdmComplexTypeAttribute), "Invalid type attribute encountered");
                    edmType = new ClrComplexType(clrType, cspaceNamespaceName, cspaceTypeName);
                }
            } 
            else
            { 
                // not a type we are interested 
                return;
            } 

            Debug.Assert(!CacheEntry.ContainsType(edmType.Identity), "This type must not be already present in the list of types for this assembly");
            // Also add this to the list of the types for this assembly
            CacheEntry.TypesInAssembly.Add(edmType); 

            // Add this to the known type map so we won't try to load it again 
            SessionData.TypesInLoading.Add(clrType.FullName, edmType); 

            // Load properties for structural type 
            if (Helper.IsStructuralType(edmType))
            {
                //Load base type only for entity type - not sure if we will allow complex type inheritance
                if (Helper.IsEntityType(edmType)) 
                {
                    TrackClosure(clrType.BaseType); 
                    AddTypeResolver( 
                        () => edmType.BaseType = ResolveBaseType(clrType.BaseType));
                } 

                // Load the properties for this type
                LoadPropertiesFromType((StructuralType)edmType);
            } 

            return; 
        } 

        private void AddTypeResolver(Action resolver) 
        {
            _referenceResolutions.Add(resolver);
        }
 
        private EdmType ResolveBaseType(Type type)
        { 
            EdmType edmType; 
            if (TryGetLoadedType(type, out edmType))
            { 
                return edmType;
            }
            return null;
        } 

        private bool TryFindNullParametersInRelationshipAttribute(EdmRelationshipAttribute roleAttribute) 
        { 
            if (roleAttribute.RelationshipName == null)
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullRelationshipNameforEdmRelationshipAttribute(SourceAssembly.FullName), null));
                return true;
            }
 
            bool nullsFound = false;
 
            if (roleAttribute.RelationshipNamespaceName == null) 
            {
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute( 
                    "RelationshipNamespaceName", roleAttribute.RelationshipName), null));
                nullsFound = true;
            }
 
            if (roleAttribute.Role1Name == null)
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute( 
                    "Role1Name", roleAttribute.RelationshipName), null));
                nullsFound = true; 
            }

            if (roleAttribute.Role1Type == null)
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                    "Role1Type", roleAttribute.RelationshipName), null)); 
                nullsFound = true; 
            }
 
            if (roleAttribute.Role2Name == null)
            {
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                    "Role2Name", roleAttribute.RelationshipName), null)); 
                nullsFound = true;
            } 
 
            if (roleAttribute.Role2Type == null)
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                    "Role2Type", roleAttribute.RelationshipName), null));
                nullsFound = true;
            } 

            return nullsFound; 
        } 

 
        private bool TryGetRelationshipEndEntityType(Type type, out EntityType entityType)
        {
            if (type == null)
            { 
                entityType = null;
                return false; 
            } 

            EdmType edmType; 
            if (!TryGetLoadedType(type, out edmType) || !Helper.IsEntityType(edmType))
            {
                entityType = null;
                return false; 
            }
            entityType = (EntityType)edmType; 
            return true; 
        }
 
        /// 
        /// Load all the property metadata of the given type
        /// 
        /// The CLR entity type 
        /// The type where properties are loaded
        ///  
        private void LoadPropertiesFromType(StructuralType structuralType) 
        {
            // Look at both public, internal, and private instanced properties declared at this type, inherited members 
            // are not looked at.  Internal and private properties are also looked at because they are also schematized fields
            PropertyInfo[] properties = structuralType.ClrType.GetProperties(PropertyReflectionBindingFlags);

            foreach (PropertyInfo property in properties) 
            {
                EdmMember newMember = null; 
                bool isEntityKeyProperty = false; //used for EdmScalarProperties only 

                // EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute 
                // are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute
                // we will just ignore it and skip to the next property.
                if (property.IsDefined(typeof(EdmRelationshipNavigationPropertyAttribute), false))
                { 
                    // keep the loop var from being lifted
                    PropertyInfo pi = property; 
                    _unresolvedNavigationProperties.Add(() => 
                            ResolveNavigationProperty(structuralType, pi));
                } 
                else if (property.IsDefined(typeof(EdmScalarPropertyAttribute), false))
                {
                    newMember = LoadScalarProperty(structuralType.ClrType, property, out isEntityKeyProperty);
                } 
                else if (property.IsDefined(typeof(EdmComplexPropertyAttribute), false))
                { 
                    TrackClosure(property.PropertyType); 
                    // keep loop var from being lifted
                    PropertyInfo local = property; 
                    AddTypeResolver(() => ResolveComplexTypeProperty(structuralType, local));
                }

                if (newMember == null) 
                {
                    // Property does not have one of the following attributes: 
                    //     EdmScalarPropertyAttribute, EdmComplexPropertyAttribute, EdmRelationshipNavigationPropertyAttribute 
                    // This means its an unmapped property and can be ignored.
                    // Or there were error encountered while loading the properties 
                    continue;
                }

                // Add the property object to the type 
                structuralType.AddMember(newMember);
 
                // Add to the entity's collection of key members 
                // Do this here instead of in the if condition above for scalar properties because
                // we want to make sure the AddMember call above did not fail before updating the key members 
                if (structuralType.BuiltInTypeKind == BuiltInTypeKind.EntityType && isEntityKeyProperty)
                {
                    ((EntityType)structuralType).AddKeyMember(newMember);
                } 
            }
        } 
 
        internal void ResolveNavigationProperty(StructuralType declaringType, PropertyInfo propertyInfo)
        { 
            Debug.Assert(propertyInfo.IsDefined(typeof(EdmRelationshipNavigationPropertyAttribute), false), "The property must have navigation property defined");

            // EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute
            // are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute 
            // we will just ignore it and skip to the next property.
            object[] relationshipPropertyAttributes = propertyInfo.GetCustomAttributes(typeof(EdmRelationshipNavigationPropertyAttribute), false); 
 
            Debug.Assert(relationshipPropertyAttributes.Length == 1, "There should be exactly one property for every navigation property");
 
            // The only valid return types from navigation properties are:
            //     (1) EntityType
            //     (2) CollectionType containing valid EntityType
 
            // If TryGetLoadedType returned false, it could mean that we couldn't validate any part of the type, or it could mean that it's a generic
            // where the main generic type was validated, but the generic type parameter was not. We can't tell the difference, so just fail 
            // with the same error message in both cases. The user will have to figure out which part of the type is wrong. 
            // We can't just rely on checking for a generic because it can lead to a scenario where we report that the type parameter is invalid
            // when really it's the main generic type. That is more confusing than reporting the full name and letting the user determine the problem. 
            EdmType propertyType;
            if (!TryGetLoadedType(propertyInfo.PropertyType, out propertyType) || !(propertyType.BuiltInTypeKind == BuiltInTypeKind.EntityType || propertyType.BuiltInTypeKind == BuiltInTypeKind.CollectionType))
            {
                // Once an error is detected the property does not need to be validated further, just add to the errors 
                // collection and continue with the next property. The failure will cause an exception to be thrown later during validation of all of the types.
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_InvalidNavPropReturnType(propertyInfo.Name, propertyInfo.DeclaringType.FullName, propertyInfo.PropertyType.FullName), null)); 
                return; 
            }
            // else we have a valid EntityType or CollectionType that contains EntityType. ResolveNonSchemaType enforces that a collection type 
            // must contain an EntityType, and if it doesn't, propertyType will be null here. If propertyType is EntityType or CollectionType we know it is valid

            // Expecting EdmRelationshipNavigationPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array
 
            EdmRelationshipNavigationPropertyAttribute attribute = (EdmRelationshipNavigationPropertyAttribute)relationshipPropertyAttributes[0];
 
            EdmMember member = null; 
            EdmType type;
            if (SessionData.TypesInLoading.TryGetValue(attribute.RelationshipNamespaceName + "." + attribute.RelationshipName, out type) && 
                Helper.IsAssociationType(type))
            {
                AssociationType relationshipType = (AssociationType)type;
                if (relationshipType != null) 
                {
                    // The return value of this property has been verified, so create the property now 
                    NavigationProperty navigationProperty = new NavigationProperty(propertyInfo.Name, TypeUsage.Create(propertyType), propertyInfo); 
                    navigationProperty.RelationshipType = relationshipType;
                    member = navigationProperty; 

                    if (relationshipType.Members[0].Name == attribute.TargetRoleName)
                    {
                        navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[0]; 
                        navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[1];
                    } 
                    else if (relationshipType.Members[1].Name == attribute.TargetRoleName) 
                    {
                        navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[1]; 
                        navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[0];
                    }
                    else
                    { 
                        SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.TargetRoleNameInNavigationPropertyNotValid(
                                                    propertyInfo.Name, propertyInfo.DeclaringType.FullName, attribute.TargetRoleName, attribute.RelationshipName), navigationProperty)); 
                        member = null; 
                    }
 
                    if (member != null &&
                        ((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType != declaringType.ClrType)
                    {
                        SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NavigationPropertyRelationshipEndTypeMismatch( 
                                                    declaringType.FullName,
                                                    navigationProperty.Name, 
                                                    relationshipType.FullName, 
                                                    navigationProperty.FromEndMember.Name,
                                                    ((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType), navigationProperty)); 
                        member = null;
                    }
                }
            } 
            else
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.RelationshipNameInNavigationPropertyNotValid( 
                                            propertyInfo.Name, propertyInfo.DeclaringType.FullName, attribute.RelationshipName), declaringType));
            } 

            if (member != null)
            {
                declaringType.AddMember(member); 
            }
        } 
 

        ///  
        /// Load the property with scalar property attribute.
        /// Note that we pass the CLR type in because in the case where the property is declared on a generic
        /// base class the DeclaringType of propert won't work for us and we need the real entity type instead.
        ///  
        /// The CLR type of the entity
        /// Metadata representing the property 
        /// True if the property forms part of the entity's key 
        /// 
        private EdmMember LoadScalarProperty(Type clrType, PropertyInfo property, out bool isEntityKeyProperty) 
        {
            Debug.Assert(property.IsDefined(typeof(EdmScalarPropertyAttribute), false), "The property must have a scalar attribute");
            EdmMember member = null;
            isEntityKeyProperty = false; 

            // Load the property type and create a new property object 
            PrimitiveType primitiveType; 
            bool nullable;
 
            // If the type could not be loaded it's definitely not a primitive type, so that's an error
            // If it could be loaded but is not a primitive that's an error as well
            if (!TryGetPrimitiveType(property.PropertyType, out nullable, out primitiveType))
            { 
                // This property does not need to be validated further, just add to the errors collection and continue with the next property
                // This failure will cause an exception to be thrown later during validation of all of the types 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ScalarPropertyNotPrimitive(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null)); 
            }
            else 
            {
                object[] attrs = property.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false);

                Debug.Assert(attrs.Length == 1, "Every property can exactly have one ScalarProperty Attribute"); 
                // Expecting EdmScalarPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array
                isEntityKeyProperty = ((EdmScalarPropertyAttribute)attrs[0]).EntityKeyProperty; 
                bool isNullable = ((EdmScalarPropertyAttribute)attrs[0]).IsNullable; 

                member = new EdmProperty(property.Name, 
                    TypeUsage.Create(primitiveType, new FacetValues { Nullable = isNullable }),
                    property, clrType.TypeHandle);

            } 
            return member;
        } 
 
        private void ResolveComplexTypeProperty(StructuralType type, PropertyInfo clrProperty)
        { 

            // Load the property type and create a new property object
            EdmType propertyType;
            // If the type could not be loaded it's definitely not a complex type, so that's an error 
            // If it could be loaded but is not a complex type that's an error as well
            if (!TryGetLoadedType(clrProperty.PropertyType, out propertyType) || propertyType.BuiltInTypeKind != BuiltInTypeKind.ComplexType) 
            { 
                // This property does not need to be validated further, just add to the errors collection and continue with the next property
                // This failure will cause an exception to be thrown later during validation of all of the types 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ComplexPropertyNotComplex(clrProperty.Name, clrProperty.DeclaringType.FullName, clrProperty.PropertyType.FullName), null));
            }
            else
            { 
                EdmProperty newProperty = new EdmProperty(clrProperty.Name,
                    TypeUsage.Create(propertyType, new FacetValues { Nullable = false }), 
                    clrProperty, type.ClrType.TypeHandle); 

                type.AddMember(newProperty); 
            }

        }
 
        private void TrackClosure(Type type)
        { 
 
            if (SourceAssembly != type.Assembly &&
                !CacheEntry.ClosureAssemblies.Contains(type.Assembly) && 
                IsSchemaAttributePresent(type.Assembly) &&
                !(type.IsGenericType &&
                  (
                    EntityUtil.IsAnICollection(type) || // EntityCollection<>, List<>, ICollection<> 
                    type.GetGenericTypeDefinition() == typeof(System.Data.Objects.DataClasses.EntityReference<>) ||
                    type.GetGenericTypeDefinition() == typeof(System.Nullable<>) 
                  ) 
                 )
                ) 
            {
                CacheEntry.ClosureAssemblies.Add(type.Assembly);
            }
 
            if (type.IsGenericType)
            { 
                foreach (Type genericArgument in type.GetGenericArguments()) 
                {
                    TrackClosure(genericArgument); 
                }
            }
        }
 
        internal static bool IsSchemaAttributePresent(Assembly assembly)
        { 
            return assembly.IsDefined(typeof(EdmSchemaAttribute), false /*inherit*/); 
        }
 
        internal static ObjectItemAssemblyLoader Create(Assembly assembly, ObjectItemLoadingSessionData sessionData)
        {
            if (ObjectItemAttributeAssemblyLoader.IsSchemaAttributePresent(assembly))
            { 
                return new ObjectItemAttributeAssemblyLoader(assembly, sessionData);
            } 
            else 
            {
                return new ObjectItemNoOpAssemblyLoader(assembly, sessionData); 
            }
        }

    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

 
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Objects.DataClasses;
using System.Diagnostics; 
using System.Reflection;
 
namespace System.Data.Metadata.Edm 
{
    ///  
    /// Class for representing a collection of items for the object layer.
    /// Most of the implemetation for actual maintainance of the collection is
    /// done by ItemCollection
    ///  
    internal sealed class ObjectItemAttributeAssemblyLoader : ObjectItemAssemblyLoader
    { 
        #region Fields 

        // list of unresolved navigation properties 
        private readonly List _unresolvedNavigationProperties = new List();
        private new MutableAssemblyCacheEntry CacheEntry { get { return (MutableAssemblyCacheEntry)base.CacheEntry; } }
        private List _referenceResolutions = new List();
 
        #endregion
 
        #region Constructor 
        internal ObjectItemAttributeAssemblyLoader(Assembly assembly, ObjectItemLoadingSessionData sessionData)
            :base(assembly, new MutableAssemblyCacheEntry(), sessionData) 
        {
            Debug.Assert(Create == sessionData.ObjectItemAssemblyLoaderFactory, "Why is there a different factory creating this class");

        } 
        #endregion
 
        #region Methods 

        internal override void OnLevel1SessionProcessing() 
        {
            foreach (Action resolve in _referenceResolutions)
            {
                resolve(); 
            }
        } 
 
        internal override void OnLevel2SessionProcessing()
        { 
            foreach (Action resolve in _unresolvedNavigationProperties)
            {
                resolve();
            } 
        }
        ///  
        /// Loads the given assembly and all the other referencd assemblies in the cache. If the assembly was already present 
        /// then it loads from the cache
        ///  
        /// 
        /// true if the assembly was already loaded in the cache
        internal override void Load()
        { 
            Debug.Assert(IsSchemaAttributePresent(SourceAssembly), "LoadAssembly shouldn't be called with assembly having no schema attribute");
            Debug.Assert(!SessionData.KnownAssemblies.Contains(SourceAssembly, SessionData.ObjectItemAssemblyLoaderFactory, SessionData.EdmItemCollection), "InternalLoadAssemblyFromCache: This assembly must not be present in the list of known assemblies"); 
 
            base.Load();
        } 

        protected override void AddToAssembliesLoaded()
        {
            SessionData.AssembliesLoaded.Add(SourceAssembly, CacheEntry); 
        }
        ///  
        /// Check to see if the type is already loaded - either in the typesInLoading, or ObjectItemCollection or 
        /// in the global cache
        ///  
        /// 
        /// 
        /// 
        private bool TryGetLoadedType(Type clrType, out EdmType edmType) 
        {
            if (SessionData.TypesInLoading.TryGetValue(clrType.FullName, out edmType) || 
                TryGetCachedEdmType(clrType, out edmType)) 
            {
                // Check to make sure the CLR type we got is the same as the given one 
                if (edmType.ClrType != clrType)
                {
                    SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NewTypeConflictsWithExistingType(
                                                clrType.AssemblyQualifiedName, edmType.ClrType.AssemblyQualifiedName), edmType)); 
                    edmType = null;
                    return false; 
                } 
                return true;
            } 


            // Let's check to see if this type is a ref type, a nullable type, or a collection type, these are the types that
            // we need to take special care of them 
            if (clrType.IsGenericType)
            { 
                Type genericType = clrType.GetGenericTypeDefinition(); 

                // Try to resolve the element type into a type object 
                EdmType elementType;
                if (!TryGetLoadedType(clrType.GetGenericArguments()[0], out elementType))
                    return false;
 
                if (typeof(System.Collections.IEnumerable).IsAssignableFrom(clrType))
                { 
                    EntityType entityType = elementType as EntityType; 
                    if (entityType == null)
                    { 
                        // return null and let the caller deal with the error handling
                        return false;
                    }
                    edmType = entityType.GetCollectionType(); 
                }
                else 
                { 
                    edmType = elementType;
                } 

                return true;
            }
 

            edmType = null; 
            return false; 
        }
 
        private bool TryGetCachedEdmType(Type clrType, out EdmType edmType)
        {
            Debug.Assert(!SessionData.TypesInLoading.ContainsKey(clrType.FullName), "This should be called only after looking in typesInLoading");
            Debug.Assert(SessionData.EdmItemErrors.Count > 0 || // had an error during loading 
                        clrType.GetCustomAttributes(typeof(EdmTypeAttribute), false /*inherit*/).Length == 0 || // not a type we track
                        SourceAssembly != clrType.Assembly, // not from this assembly 
                        "Given that we don't have any error, if the type is part of this assembly, it should not be loaded from the cache"); 

            ImmutableAssemblyCacheEntry immutableCacheEntry; 
            if (SessionData.LockedAssemblyCache.TryGetValue(clrType.Assembly, out immutableCacheEntry))
            {
                Debug.Assert(SessionData.KnownAssemblies.Contains(clrType.Assembly, SessionData.LoaderCookie, SessionData.EdmItemCollection), "We should only be loading things directly from the cache if they are already in the collection");
                return immutableCacheEntry.TryGetEdmType(clrType.FullName, out edmType); 
            }
 
            edmType = null; 
            return false;
        } 

        #endregion
        /// 
        /// Loads the set of types from the given assembly and adds it to the given list of types 
        /// 
        /// context containing information for loading 
        protected override void LoadTypesFromAssembly() 
        {
            Debug.Assert(CacheEntry.TypesInAssembly.Count == 0); 

            LoadRelationshipTypes();

            // Loop through each type in the assembly and process it 
            foreach (Type type in SourceAssembly.GetTypes())
            { 
                // If the type doesn't have the same EdmTypeAttribute defined, then it's not a special type 
                // that we care about, skip it.
                if (!type.IsDefined(typeof(EdmTypeAttribute), false)) 
                {
                    continue;
                }
 
                // Load the metadata for this type
                LoadType(type); 
            } 

            if (_referenceResolutions.Count != 0) 
            {
                SessionData.RegisterForLevel1PostSessionProcessing(this);
            }
 
            if (_unresolvedNavigationProperties.Count != 0)
            { 
                SessionData.RegisterForLevel2PostSessionProcessing(this); 
            }
        } 

        /// 
        /// This method loads all the relationship type that this entity takes part in
        ///  
        /// 
        ///  
        private void LoadRelationshipTypes() 
        {
            foreach (EdmRelationshipAttribute roleAttribute in SourceAssembly.GetCustomAttributes(typeof(EdmRelationshipAttribute), false /*inherit*/)) 
            {
                // Check if there is an entry already with this name
                if (TryFindNullParametersInRelationshipAttribute(roleAttribute))
                { 
                    // don't give more errors for these same bad parameters
                    continue; 
                } 

                bool errorEncountered = false; 

                // return error if the role names are the same
                if (roleAttribute.Role1Name == roleAttribute.Role2Name)
                { 
                    SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.SameRoleNameOnRelationshipAttribute(roleAttribute.RelationshipName, roleAttribute.Role2Name),
                               null)); 
                    errorEncountered = true; 
                }
 

                if (!errorEncountered)
                {
                    AssociationType associationType = new AssociationType(roleAttribute.RelationshipName, roleAttribute.RelationshipNamespaceName, roleAttribute.IsForeignKey, DataSpace.OSpace); 
                    SessionData.TypesInLoading.Add(associationType.FullName, associationType);
                    TrackClosure(roleAttribute.Role1Type); 
                    TrackClosure(roleAttribute.Role2Type); 

                    // prevent lifting of loop vars 
                    string r1Name = roleAttribute.Role1Name;
                    Type r1Type = roleAttribute.Role1Type;
                    RelationshipMultiplicity r1Multiplicity = roleAttribute.Role1Multiplicity;
                    AddTypeResolver(() => 
                        ResolveAssociationEnd(associationType, r1Name, r1Type, r1Multiplicity));
 
                    // prevent lifting of loop vars 
                    string r2Name = roleAttribute.Role2Name;
                    Type r2Type = roleAttribute.Role2Type; 
                    RelationshipMultiplicity r2Multiplicity = roleAttribute.Role2Multiplicity;
                    AddTypeResolver(() =>
                        ResolveAssociationEnd(associationType, r2Name, r2Type, r2Multiplicity));
 
                    // get assembly entry and add association type to the list of types in the assembly
                    Debug.Assert(!CacheEntry.ContainsType(associationType.FullName), "Relationship type must not be present in the list of types"); 
                    CacheEntry.TypesInAssembly.Add(associationType); 
                }
            } 
        }

        private void ResolveAssociationEnd(AssociationType associationType, string roleName, Type clrType, RelationshipMultiplicity multiplicity)
        { 
            EntityType entityType;
            if (!TryGetRelationshipEndEntityType(clrType, out entityType)) 
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(associationType.Name, roleName, clrType),
                           null)); 
                return;
            }
            associationType.AddKeyMember(new AssociationEndMember(roleName, entityType.GetReferenceType(), multiplicity));
        } 
        /// 
        /// Load metadata of the given type - when you call this method, you should check and make sure that the type has 
        /// edm attribute. If it doesn't,we won't load the type and it will be returned as null 
        /// 
        ///  
        /// 
        /// 
        private void LoadType(Type clrType)
        { 
            Debug.Assert(clrType.Assembly == SourceAssembly, "Why are we loading a type that is not in our assembly?");
            Debug.Assert(!SessionData.TypesInLoading.ContainsKey(clrType.FullName), "Trying to load a type that is already loaded???"); 
 
            EdmType edmType = null;
 
            EdmTypeAttribute[] typeAttributes = (EdmTypeAttribute[])clrType.GetCustomAttributes(typeof(EdmTypeAttribute), false /*inherit*/);

            // the CLR doesn't allow types to have duplicate/multiple attribute declarations
 
            if (typeAttributes.Length != 0)
            { 
                if (clrType.IsNested) 
                {
                    SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NestedClassNotSupported(clrType.FullName, clrType.Assembly.FullName), null)); 
                    return;
                }
                EdmTypeAttribute typeAttribute = typeAttributes[0];
                string cspaceTypeName = String.IsNullOrEmpty(typeAttribute.Name) ? clrType.Name : typeAttribute.Name; 
                if (String.IsNullOrEmpty(typeAttribute.NamespaceName) && clrType.Namespace == null)
                { 
                    SessionData.EdmItemErrors.Add(new EdmItemError(Strings.Validator_TypeHasNoNamespace, edmType)); 
                    return;
                } 

                string cspaceNamespaceName = String.IsNullOrEmpty(typeAttribute.NamespaceName) ? clrType.Namespace : typeAttribute.NamespaceName;

                if (typeAttribute.GetType() == typeof(EdmEntityTypeAttribute)) 
                {
                    edmType = new ClrEntityType(clrType, cspaceNamespaceName, cspaceTypeName); 
                } 
                else
                { 
                    Debug.Assert(typeAttribute.GetType() == typeof(EdmComplexTypeAttribute), "Invalid type attribute encountered");
                    edmType = new ClrComplexType(clrType, cspaceNamespaceName, cspaceTypeName);
                }
            } 
            else
            { 
                // not a type we are interested 
                return;
            } 

            Debug.Assert(!CacheEntry.ContainsType(edmType.Identity), "This type must not be already present in the list of types for this assembly");
            // Also add this to the list of the types for this assembly
            CacheEntry.TypesInAssembly.Add(edmType); 

            // Add this to the known type map so we won't try to load it again 
            SessionData.TypesInLoading.Add(clrType.FullName, edmType); 

            // Load properties for structural type 
            if (Helper.IsStructuralType(edmType))
            {
                //Load base type only for entity type - not sure if we will allow complex type inheritance
                if (Helper.IsEntityType(edmType)) 
                {
                    TrackClosure(clrType.BaseType); 
                    AddTypeResolver( 
                        () => edmType.BaseType = ResolveBaseType(clrType.BaseType));
                } 

                // Load the properties for this type
                LoadPropertiesFromType((StructuralType)edmType);
            } 

            return; 
        } 

        private void AddTypeResolver(Action resolver) 
        {
            _referenceResolutions.Add(resolver);
        }
 
        private EdmType ResolveBaseType(Type type)
        { 
            EdmType edmType; 
            if (TryGetLoadedType(type, out edmType))
            { 
                return edmType;
            }
            return null;
        } 

        private bool TryFindNullParametersInRelationshipAttribute(EdmRelationshipAttribute roleAttribute) 
        { 
            if (roleAttribute.RelationshipName == null)
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullRelationshipNameforEdmRelationshipAttribute(SourceAssembly.FullName), null));
                return true;
            }
 
            bool nullsFound = false;
 
            if (roleAttribute.RelationshipNamespaceName == null) 
            {
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute( 
                    "RelationshipNamespaceName", roleAttribute.RelationshipName), null));
                nullsFound = true;
            }
 
            if (roleAttribute.Role1Name == null)
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute( 
                    "Role1Name", roleAttribute.RelationshipName), null));
                nullsFound = true; 
            }

            if (roleAttribute.Role1Type == null)
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                    "Role1Type", roleAttribute.RelationshipName), null)); 
                nullsFound = true; 
            }
 
            if (roleAttribute.Role2Name == null)
            {
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                    "Role2Name", roleAttribute.RelationshipName), null)); 
                nullsFound = true;
            } 
 
            if (roleAttribute.Role2Type == null)
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                    "Role2Type", roleAttribute.RelationshipName), null));
                nullsFound = true;
            } 

            return nullsFound; 
        } 

 
        private bool TryGetRelationshipEndEntityType(Type type, out EntityType entityType)
        {
            if (type == null)
            { 
                entityType = null;
                return false; 
            } 

            EdmType edmType; 
            if (!TryGetLoadedType(type, out edmType) || !Helper.IsEntityType(edmType))
            {
                entityType = null;
                return false; 
            }
            entityType = (EntityType)edmType; 
            return true; 
        }
 
        /// 
        /// Load all the property metadata of the given type
        /// 
        /// The CLR entity type 
        /// The type where properties are loaded
        ///  
        private void LoadPropertiesFromType(StructuralType structuralType) 
        {
            // Look at both public, internal, and private instanced properties declared at this type, inherited members 
            // are not looked at.  Internal and private properties are also looked at because they are also schematized fields
            PropertyInfo[] properties = structuralType.ClrType.GetProperties(PropertyReflectionBindingFlags);

            foreach (PropertyInfo property in properties) 
            {
                EdmMember newMember = null; 
                bool isEntityKeyProperty = false; //used for EdmScalarProperties only 

                // EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute 
                // are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute
                // we will just ignore it and skip to the next property.
                if (property.IsDefined(typeof(EdmRelationshipNavigationPropertyAttribute), false))
                { 
                    // keep the loop var from being lifted
                    PropertyInfo pi = property; 
                    _unresolvedNavigationProperties.Add(() => 
                            ResolveNavigationProperty(structuralType, pi));
                } 
                else if (property.IsDefined(typeof(EdmScalarPropertyAttribute), false))
                {
                    newMember = LoadScalarProperty(structuralType.ClrType, property, out isEntityKeyProperty);
                } 
                else if (property.IsDefined(typeof(EdmComplexPropertyAttribute), false))
                { 
                    TrackClosure(property.PropertyType); 
                    // keep loop var from being lifted
                    PropertyInfo local = property; 
                    AddTypeResolver(() => ResolveComplexTypeProperty(structuralType, local));
                }

                if (newMember == null) 
                {
                    // Property does not have one of the following attributes: 
                    //     EdmScalarPropertyAttribute, EdmComplexPropertyAttribute, EdmRelationshipNavigationPropertyAttribute 
                    // This means its an unmapped property and can be ignored.
                    // Or there were error encountered while loading the properties 
                    continue;
                }

                // Add the property object to the type 
                structuralType.AddMember(newMember);
 
                // Add to the entity's collection of key members 
                // Do this here instead of in the if condition above for scalar properties because
                // we want to make sure the AddMember call above did not fail before updating the key members 
                if (structuralType.BuiltInTypeKind == BuiltInTypeKind.EntityType && isEntityKeyProperty)
                {
                    ((EntityType)structuralType).AddKeyMember(newMember);
                } 
            }
        } 
 
        internal void ResolveNavigationProperty(StructuralType declaringType, PropertyInfo propertyInfo)
        { 
            Debug.Assert(propertyInfo.IsDefined(typeof(EdmRelationshipNavigationPropertyAttribute), false), "The property must have navigation property defined");

            // EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute
            // are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute 
            // we will just ignore it and skip to the next property.
            object[] relationshipPropertyAttributes = propertyInfo.GetCustomAttributes(typeof(EdmRelationshipNavigationPropertyAttribute), false); 
 
            Debug.Assert(relationshipPropertyAttributes.Length == 1, "There should be exactly one property for every navigation property");
 
            // The only valid return types from navigation properties are:
            //     (1) EntityType
            //     (2) CollectionType containing valid EntityType
 
            // If TryGetLoadedType returned false, it could mean that we couldn't validate any part of the type, or it could mean that it's a generic
            // where the main generic type was validated, but the generic type parameter was not. We can't tell the difference, so just fail 
            // with the same error message in both cases. The user will have to figure out which part of the type is wrong. 
            // We can't just rely on checking for a generic because it can lead to a scenario where we report that the type parameter is invalid
            // when really it's the main generic type. That is more confusing than reporting the full name and letting the user determine the problem. 
            EdmType propertyType;
            if (!TryGetLoadedType(propertyInfo.PropertyType, out propertyType) || !(propertyType.BuiltInTypeKind == BuiltInTypeKind.EntityType || propertyType.BuiltInTypeKind == BuiltInTypeKind.CollectionType))
            {
                // Once an error is detected the property does not need to be validated further, just add to the errors 
                // collection and continue with the next property. The failure will cause an exception to be thrown later during validation of all of the types.
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_InvalidNavPropReturnType(propertyInfo.Name, propertyInfo.DeclaringType.FullName, propertyInfo.PropertyType.FullName), null)); 
                return; 
            }
            // else we have a valid EntityType or CollectionType that contains EntityType. ResolveNonSchemaType enforces that a collection type 
            // must contain an EntityType, and if it doesn't, propertyType will be null here. If propertyType is EntityType or CollectionType we know it is valid

            // Expecting EdmRelationshipNavigationPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array
 
            EdmRelationshipNavigationPropertyAttribute attribute = (EdmRelationshipNavigationPropertyAttribute)relationshipPropertyAttributes[0];
 
            EdmMember member = null; 
            EdmType type;
            if (SessionData.TypesInLoading.TryGetValue(attribute.RelationshipNamespaceName + "." + attribute.RelationshipName, out type) && 
                Helper.IsAssociationType(type))
            {
                AssociationType relationshipType = (AssociationType)type;
                if (relationshipType != null) 
                {
                    // The return value of this property has been verified, so create the property now 
                    NavigationProperty navigationProperty = new NavigationProperty(propertyInfo.Name, TypeUsage.Create(propertyType), propertyInfo); 
                    navigationProperty.RelationshipType = relationshipType;
                    member = navigationProperty; 

                    if (relationshipType.Members[0].Name == attribute.TargetRoleName)
                    {
                        navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[0]; 
                        navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[1];
                    } 
                    else if (relationshipType.Members[1].Name == attribute.TargetRoleName) 
                    {
                        navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[1]; 
                        navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[0];
                    }
                    else
                    { 
                        SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.TargetRoleNameInNavigationPropertyNotValid(
                                                    propertyInfo.Name, propertyInfo.DeclaringType.FullName, attribute.TargetRoleName, attribute.RelationshipName), navigationProperty)); 
                        member = null; 
                    }
 
                    if (member != null &&
                        ((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType != declaringType.ClrType)
                    {
                        SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NavigationPropertyRelationshipEndTypeMismatch( 
                                                    declaringType.FullName,
                                                    navigationProperty.Name, 
                                                    relationshipType.FullName, 
                                                    navigationProperty.FromEndMember.Name,
                                                    ((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType), navigationProperty)); 
                        member = null;
                    }
                }
            } 
            else
            { 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.RelationshipNameInNavigationPropertyNotValid( 
                                            propertyInfo.Name, propertyInfo.DeclaringType.FullName, attribute.RelationshipName), declaringType));
            } 

            if (member != null)
            {
                declaringType.AddMember(member); 
            }
        } 
 

        ///  
        /// Load the property with scalar property attribute.
        /// Note that we pass the CLR type in because in the case where the property is declared on a generic
        /// base class the DeclaringType of propert won't work for us and we need the real entity type instead.
        ///  
        /// The CLR type of the entity
        /// Metadata representing the property 
        /// True if the property forms part of the entity's key 
        /// 
        private EdmMember LoadScalarProperty(Type clrType, PropertyInfo property, out bool isEntityKeyProperty) 
        {
            Debug.Assert(property.IsDefined(typeof(EdmScalarPropertyAttribute), false), "The property must have a scalar attribute");
            EdmMember member = null;
            isEntityKeyProperty = false; 

            // Load the property type and create a new property object 
            PrimitiveType primitiveType; 
            bool nullable;
 
            // If the type could not be loaded it's definitely not a primitive type, so that's an error
            // If it could be loaded but is not a primitive that's an error as well
            if (!TryGetPrimitiveType(property.PropertyType, out nullable, out primitiveType))
            { 
                // This property does not need to be validated further, just add to the errors collection and continue with the next property
                // This failure will cause an exception to be thrown later during validation of all of the types 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ScalarPropertyNotPrimitive(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null)); 
            }
            else 
            {
                object[] attrs = property.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false);

                Debug.Assert(attrs.Length == 1, "Every property can exactly have one ScalarProperty Attribute"); 
                // Expecting EdmScalarPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array
                isEntityKeyProperty = ((EdmScalarPropertyAttribute)attrs[0]).EntityKeyProperty; 
                bool isNullable = ((EdmScalarPropertyAttribute)attrs[0]).IsNullable; 

                member = new EdmProperty(property.Name, 
                    TypeUsage.Create(primitiveType, new FacetValues { Nullable = isNullable }),
                    property, clrType.TypeHandle);

            } 
            return member;
        } 
 
        private void ResolveComplexTypeProperty(StructuralType type, PropertyInfo clrProperty)
        { 

            // Load the property type and create a new property object
            EdmType propertyType;
            // If the type could not be loaded it's definitely not a complex type, so that's an error 
            // If it could be loaded but is not a complex type that's an error as well
            if (!TryGetLoadedType(clrProperty.PropertyType, out propertyType) || propertyType.BuiltInTypeKind != BuiltInTypeKind.ComplexType) 
            { 
                // This property does not need to be validated further, just add to the errors collection and continue with the next property
                // This failure will cause an exception to be thrown later during validation of all of the types 
                SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ComplexPropertyNotComplex(clrProperty.Name, clrProperty.DeclaringType.FullName, clrProperty.PropertyType.FullName), null));
            }
            else
            { 
                EdmProperty newProperty = new EdmProperty(clrProperty.Name,
                    TypeUsage.Create(propertyType, new FacetValues { Nullable = false }), 
                    clrProperty, type.ClrType.TypeHandle); 

                type.AddMember(newProperty); 
            }

        }
 
        private void TrackClosure(Type type)
        { 
 
            if (SourceAssembly != type.Assembly &&
                !CacheEntry.ClosureAssemblies.Contains(type.Assembly) && 
                IsSchemaAttributePresent(type.Assembly) &&
                !(type.IsGenericType &&
                  (
                    EntityUtil.IsAnICollection(type) || // EntityCollection<>, List<>, ICollection<> 
                    type.GetGenericTypeDefinition() == typeof(System.Data.Objects.DataClasses.EntityReference<>) ||
                    type.GetGenericTypeDefinition() == typeof(System.Nullable<>) 
                  ) 
                 )
                ) 
            {
                CacheEntry.ClosureAssemblies.Add(type.Assembly);
            }
 
            if (type.IsGenericType)
            { 
                foreach (Type genericArgument in type.GetGenericArguments()) 
                {
                    TrackClosure(genericArgument); 
                }
            }
        }
 
        internal static bool IsSchemaAttributePresent(Assembly assembly)
        { 
            return assembly.IsDefined(typeof(EdmSchemaAttribute), false /*inherit*/); 
        }
 
        internal static ObjectItemAssemblyLoader Create(Assembly assembly, ObjectItemLoadingSessionData sessionData)
        {
            if (ObjectItemAttributeAssemblyLoader.IsSchemaAttributePresent(assembly))
            { 
                return new ObjectItemAttributeAssemblyLoader(assembly, sessionData);
            } 
            else 
            {
                return new ObjectItemNoOpAssemblyLoader(assembly, sessionData); 
            }
        }

    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK