ObjectItemCollectionAssemblyCacheEntry.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Metadata / ObjectItemCollectionAssemblyCacheEntry.cs / 2 / ObjectItemCollectionAssemblyCacheEntry.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections; 
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Serialization;
using System.Xml; 
using System.Xml.Schema;
using System.Data.Common.Utils; 
using System.Diagnostics; 
using System.Collections.ObjectModel;
using System.Threading; 
// Using an alias for this because a lot of names in this namespace conflicts with names in metadata
using DataClasses = System.Data.Objects.DataClasses;
using System.Globalization;
using System.Data.Entity; 
using System.Data.Common;
 
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
    ///  
    public sealed partial class ObjectItemCollection : ItemCollection
    { 
        #region PrivateNestedClass 
        private class AssemblyCacheEntry
        { 
            #region Fields
            private readonly List _typesInAssembly;       // types in "this" assembly
            private readonly List _referencedAssemblies; // other assemblies referenced by "this" assembly
 
            private static object _assemblyCacheLock = new object();
 
            //List of assemblies having view gen attribute. We cache these things if we discover 
            //these assemblies while looking for O-space metadata.
            private static IList s_viewGenAssemblies = new ThreadSafeList(); 

            #endregion

            #region Nested classes 

            private class LoadingContext 
            { 
                #region Fields
                // all the types that we encountered while loading - this may contain types from various assemblies 
                private readonly Dictionary _typesInLoading;

                // list of errors encountered during loading
                private readonly List _errors; 

                // list of unresolved navigation properties 
                private readonly List _unresolvedNavigationProperties = new List(); 

                // keep the list of new assemblies that got loaded in this load assembly call. The region why we need to keep a seperate 
                // assembly is that if we need to keep track of errors, and if there are no errors, then only add the list of assemblies
                // to the global cache. Hence global cache is never polluted with invalid assemblies
                private readonly Dictionary _listOfAssembliesLoaded = new Dictionary();
 
                // Current assembly whose type we are loading
                private Assembly _currentAssembly; 
 
                // Assembly Cache Entry corresponding to the current assembly
                private AssemblyCacheEntry _currentCacheEntry; 

                // Indicates if this assembly is already loaded in the cache
                private bool _isAssemblyLoadedFromGlobalCache;
 
                // Global Assembly Cache
                private readonly static Dictionary s_globalAssemblyCache = new Dictionary(); 
 
                // List of known assemblies - this list is initially passed by the caller and we keep adding to it, as and when we load
                // an assembly 
                private readonly Dictionary _knownAssemblies;

                #endregion
 
                #region Constructor
                internal LoadingContext(Assembly assembly, Dictionary knownAssemblies) 
                { 
                    _typesInLoading = new Dictionary(StringComparer.Ordinal);
                    _errors = new List(); 
                    _knownAssemblies = knownAssemblies;

                    UpdateCurrentAssembly(assembly, false/*mustAssemblyBeAlreadyLoaded*/);
                } 
                #endregion
 
                #region Properties 

                internal Dictionary TypesInLoading { get { return _typesInLoading; } } 

                internal List EdmItemError { get { return _errors; } }

                internal List UnresolvedNavigationProperties { get { return _unresolvedNavigationProperties; } } 

                internal Assembly CurrentAssembly { get { return _currentAssembly; } } 
 
                internal AssemblyCacheEntry AssemblyCacheEntry { get { return _currentCacheEntry; } }
 
                internal bool IsAssemblyAlreadyLoadedInCache { get { return _isAssemblyLoadedFromGlobalCache; } }

                internal Dictionary KnownAssemblies { get { return _knownAssemblies; } }
 
                #endregion
 
                #region Methods 

                ///  
                /// Check to see if the type is already loaded - either in the typesInLoading, or ObjectItemCollection or
                /// in the global cache
                /// 
                ///  
                /// 
                ///  
                internal bool IsTypeAlreadyLoaded(Type clrType, out EdmType edmType) 
                {
                    edmType = null; 
                    bool isPresentInAssemblyCache = false;
                    bool isPresentInTypesInLoading = false;

                    if (!clrType.IsGenericType && ((isPresentInTypesInLoading = TypesInLoading.TryGetValue(clrType.FullName, out edmType)) || 
                                                   (isPresentInAssemblyCache = IsTypeAlreadyInCache(clrType, out edmType))))
                    { 
                        // If the type is primitive type, just return the type 
                        if (!Helper.IsPrimitiveType(edmType))
                        { 
                            Debug.Assert(!ShouldFilterAssembly(clrType.Assembly.FullName), "Since the type is already loaded, the assembly must have a schema attribute");
                            Debug.Assert(ObjectItemCollection.IsSchemaAttributePresent(clrType.Assembly), "Since the type is already loaded, the assembly must have a schema attribute");

                            // If the type is not present in the current assembly, make sure you add the type's assembly 
                            // as one of the referenced assemblies
                            if (clrType.Assembly != _currentAssembly) 
                            { 
                                if (!_currentCacheEntry._referencedAssemblies.Contains(clrType.Assembly))
                                { 
                                    _currentCacheEntry._referencedAssemblies.Add(clrType.Assembly);
                                }
                            }
                            // If the base type BT1 of a type T1 is present in another assembly, we just load the base type from that assembly and add 
                            // that assembly to the list of referenced assemblies. When you come to loading the referenced assembly, the type BT1
                            // is already present in TypeInLoading, and hence we need to add it to the list of types in assembly 
                            else if (isPresentInTypesInLoading && !_currentCacheEntry.ContainsType(edmType.Identity)) 
                            {
                                _currentCacheEntry._typesInAssembly.Add(edmType); 
                            }

                            // If the type was loaded from the global cache, then we need to find if this assembly is already loaded. If yes,
                            // then we don't need to add that type in typesInLoading 
                            if (isPresentInAssemblyCache && !KnownAssemblies.ContainsKey(clrType.Assembly))
                            { 
                                TypesInLoading.Add(clrType.FullName, edmType); 
                            }
                        } 
                    }

                    return (edmType != null);
                } 

                ///  
                /// Returns if the types is already loaded in the cache 
                /// 
                ///  
                /// 
                /// 
                private bool IsTypeAlreadyInCache(Type clrType, out EdmType edmType)
                { 
                    AssemblyCacheEntry cacheEntry;
                    edmType = null; 
 
                    Debug.Assert(!_typesInLoading.ContainsKey(clrType.FullName), "This should be called only after looking in typesInLoading");
                    Debug.Assert(clrType.Assembly != _currentAssembly || !_currentCacheEntry.ContainsType(clrType.FullName), "The type must never be present in the current assembly list"); 

                    if (clrType.Assembly == _currentAssembly)
                    {
                        return false; 
                    }
 
                    if (s_globalAssemblyCache.TryGetValue(clrType.Assembly, out cacheEntry)) 
                    {
                        return cacheEntry.TryGetEdmType(clrType.FullName, out edmType); 
                    }
                    else if (_listOfAssembliesLoaded.TryGetValue(clrType.Assembly, out cacheEntry))
                    {
                        return cacheEntry.TryGetEdmType(clrType.FullName, out edmType); 
                    }
 
                    return false; 
                }
 
                /// 
                /// Update the current assembly for the loading context. If the second parameter is true, assert that
                /// the assembly must be present in the global cache. this is to make sure if a assembly was already
                /// present in the global cache, all its dependent assemblies must also be present in the global cache 
                /// This method checks if the given assembly is present in the global cache, if yes, its loads from there
                /// otherwise creates a new AssemblyCacheEntry for this assembly. 
                /// Also it adds the earlier current assembly, into its local cache. The reason for doing this is that until 
                /// we have loaded all the assemblies for this context and made sure that there are no errors, then we need
                /// to update the global cache 
                /// 
                /// 
                /// 
                internal void UpdateCurrentAssembly(Assembly assembly, bool mustAssemblyBeAlreadyLoaded) 
                {
                    Debug.Assert(assembly != null, "Current Assembly can't be set to null"); 
                    Debug.Assert(!mustAssemblyBeAlreadyLoaded || s_globalAssemblyCache.ContainsKey(assembly), "The assembly must be loaded in the cache"); 

                    // Update the current assembly 
                    _currentAssembly = assembly;

                    if (mustAssemblyBeAlreadyLoaded)
                    { 
                        // check if the assembly is already loaded in the cache
                        _currentCacheEntry = s_globalAssemblyCache[assembly]; 
                        _isAssemblyLoadedFromGlobalCache = true; 
                    }
                    else if (s_globalAssemblyCache.TryGetValue(assembly, out _currentCacheEntry)) 
                    {
                        _isAssemblyLoadedFromGlobalCache = true;
                    }
                    // If the assemblies have circular dependencies then the assembly might have been added in the local cache 
                    else
                    { 
                        if (!_listOfAssembliesLoaded.TryGetValue(assembly, out _currentCacheEntry)) 
                        {
                            _currentCacheEntry = new AssemblyCacheEntry(); 
                            _listOfAssembliesLoaded.Add(_currentAssembly, _currentCacheEntry);
                        }
                        _isAssemblyLoadedFromGlobalCache = false;
                    } 
                }
 
                // Add all assemblies to the global cache if there are no errors 
                internal void UpdateCacheWithAssembliesLoaded()
                { 
                    if (_errors.Count == 0)
                    {
                        foreach (KeyValuePair entry in _listOfAssembliesLoaded)
                        { 
                            // Add all the assemblies from the loading context to the global cache
                            s_globalAssemblyCache.Add(entry.Key, entry.Value); 
                        } 

                        // Remove all entries from transient cache 
                        _listOfAssembliesLoaded.Clear();
                    }
                }
                #endregion 
            }
            private class NavigationPropertyInfo 
            { 
                private StructuralType _declaringType;
                private PropertyInfo _propertyInfo; 
                private EdmType _propertyType;
                private DataClasses.EdmRelationshipNavigationPropertyAttribute _attribute;

                internal NavigationPropertyInfo( 
                        StructuralType declaringType,
                        PropertyInfo propertyInfo, 
                        EdmType propertyType, 
                        DataClasses.EdmRelationshipNavigationPropertyAttribute attribute)
                { 
                    _declaringType = declaringType;
                    _propertyInfo = propertyInfo;
                    _propertyType = propertyType;
                    _attribute = attribute; 
                }
 
                internal void ResolveNavigationProperty(LoadingContext context) 
                {
                    EdmMember member = null; 
                    EdmType type;
                    if (context.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
                            { 
                                context.EdmItemError.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) 
                            {
                                context.EdmItemError.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 
                    {
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RelationshipNameInNavigationPropertyNotValid( 
                                                    _propertyInfo.Name, _propertyInfo.DeclaringType.FullName, _attribute.RelationshipName), _declaringType));
                    }

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

            }

 
            #endregion
 
            #region Constructor 
            public AssemblyCacheEntry()
            { 
                _typesInAssembly = new List();
                _referencedAssemblies = new List();
            }
            #endregion 

            #region Internal Methods (Entry points to the Cache) 
 
            internal static IList ViewGenerationAssemblies
            { 
                get
                {
                    return s_viewGenAssemblies;
                } 
            }
 
            internal static void LoadAssemblyFromCache(Assembly assembly, bool loadReferencedAssemblies, 
                Dictionary knownAssemblies, out Dictionary typesInLoading, out List errors)
            { 
                Debug.Assert(!ShouldFilterAssembly(assembly.FullName), "LoadAssemblyFromCache should be called on assembly having non-reserved public key token");
                typesInLoading = null;
                errors = null;
 
                lock (_assemblyCacheLock)
                { 
                    // This function loads all the types from the given assembly and the dependent assemblies. We only try to load assemblies 
                    // that are requried for type closure. This is different from referenced asssemblies. If the assembly is already loaded,
                    // then the assembly is just copied from the cache 
                    LoadingContext context = new LoadingContext(assembly, knownAssemblies);

                    // Loads the current assembly and all the dependent assemblies (required for type closure)
                    if (!context.KnownAssemblies.ContainsKey(context.CurrentAssembly) && IsSchemaAttributePresent(context.CurrentAssembly)) 
                    {
                        InternalLoadAssemblyFromCache(context); 
                    } 

                    if (loadReferencedAssemblies) 
                    {
                        InternalLoadAllReferencedAssemblies(context);
                    }
 
                    // resolve navigation properties that showed up
                    // before the relationships that they use showed up 
                    ResolveNavigationProperties(context); 

                    // do the validation for the all the new types 
                    // Now, perform validation on all the new types
                    EdmValidator validator = new EdmValidator();
                    validator.SkipReadOnlyItems = true;
                    validator.Validate(context.TypesInLoading.Values, context.EdmItemError); 

                    // Update the global cache if there are no errors 
                    context.UpdateCacheWithAssembliesLoaded(); 

                    // Update the out parameters once you are done with loading 
                    typesInLoading = context.TypesInLoading;
                    errors = context.EdmItemError;

                    if (typesInLoading != null && typesInLoading.Count > 0) 
                    {
                        foreach (EdmType edmType in typesInLoading.Values) 
                        { 
                            edmType.SetReadOnly();
                        } 
                    }
                }
            }
 
            internal static Assembly SafeLoadReferencedAssembly(string assemblyFullName)
            { 
                Assembly referencedAssembly = null; 

                try 
                {
                    referencedAssembly = Assembly.Load(assemblyFullName);
                }
                catch (System.IO.FileNotFoundException) 
                {
                    // See 552932: ObjectItemCollection: fails on referenced asseblies that are not available 
                } 

                return referencedAssembly; 
            }
            #endregion

            #region Private Methods 
            private bool TryGetEdmType(string typeName, out EdmType edmType)
            { 
                edmType = null; 
                foreach (EdmType loadedEdmType in this._typesInAssembly)
                { 
                    if (loadedEdmType.Identity == typeName)
                    {
                        edmType = loadedEdmType;
                        break; 
                    }
                } 
                return (edmType != null); 
            }
 
            private bool ContainsType(string typeName)
            {
                EdmType edmType = null;
                return TryGetEdmType(typeName, out edmType); 
            }
 
            private static void InternalLoadAllReferencedAssemblies(LoadingContext context) 
            {
                // We will traverse through all the statically linked assemblies and their dependencies. 
                // Only assemblies with the EdmSchemaAttribute will be loaded and rest will be ignored

                // Even if the schema attribute is missing, we should still check all the dependent assemblies
                // any of the dependent assemblies can have the schema attribute 

                // After the given assembly has been loaded, check on the flag in _knownAssemblies to see if it has already 
                // been recursively loaded. The flag can be true if it was already loaded before this function was called 
                foreach (AssemblyName asmName in context.CurrentAssembly.GetReferencedAssemblies())
                { 
                    string assemblyFullName = asmName.FullName;
                    if (!ShouldFilterAssembly(assemblyFullName))
                    {
                        // filter out "known" assemblies to prevent unnecessary loading 
                        EntityBid.Trace(" loadededAssembly='%ls'\n", assemblyFullName);
 
                        Assembly referencedAssembly = SafeLoadReferencedAssembly(assemblyFullName); 
                        if (referencedAssembly == null)
                        { 
                            continue;
                        }

                        // Mark the assembly as known assembly, and since we are loading all the referenced assemblies, 
                        // mark the value to the true
                        context.UpdateCurrentAssembly(referencedAssembly, false/*mustAlreadyBeLoaded*/); 
 
                        // Check if the assembly is already loaded
                        bool areReferencedAssembliesLoaded; 
                        if (context.KnownAssemblies.TryGetValue(referencedAssembly, out areReferencedAssembliesLoaded))
                        {
                            // If all the referenced assemblies are already loaded, don't need to do anything
                            if (areReferencedAssembliesLoaded) 
                            {
                                continue; 
                            } 
                        }
                        // Load this assembly if the schema attrbute is present 
                        else if (ObjectItemCollection.IsSchemaAttributePresent(referencedAssembly))
                        {
                            InternalLoadAssemblyFromCache(context);
                        } 

                        // We need to add this assembly to the list of known assemblies before we start 
                        // analyzing the referenced assemblies, since there could be circular reference 
                        // and we need to detect that and break the loop
                        context.KnownAssemblies[referencedAssembly] = true; 
                        InternalLoadAllReferencedAssemblies(context);
                    }
                }
            } 

            ///  
            /// 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
            private static bool InternalLoadAssemblyFromCache(LoadingContext context)
            { 
                Debug.Assert(!ShouldFilterAssembly(context.CurrentAssembly.FullName), "LoadAssemblyFromCache should be called on assembly having non-reserved public key token");
                Debug.Assert(IsSchemaAttributePresent(context.CurrentAssembly), "LoadAssembly shouldn't be called with assembly having no schema attribute"); 
                Debug.Assert(!context.KnownAssemblies.ContainsKey(context.CurrentAssembly), "InternalLoadAssemblyFromCache: This assembly must not be present in the list of known assemblies"); 

                bool areAssembliesLoadedFromCache = context.IsAssemblyAlreadyLoadedInCache; 

                // Check if the assembly has been loaded in the cache then:
                // 1) Add EdmTypes described in ----semblyCacheEntry.TypesInAssembly
                // 2) Add Assemblies described by AssemblyCacheEntry.ReferenceAssembly, check to make sure that it's not already loaded in ObjectItemCollection 
                if (context.IsAssemblyAlreadyLoadedInCache)
                { 
                    foreach (EdmType type in context.AssemblyCacheEntry._typesInAssembly) 
                    {
                        if (!context.TypesInLoading.ContainsKey(type.Identity)) 
                        {
                            context.TypesInLoading.Add(type.Identity, type);
                        }
                    } 
                }
                else 
                { 
                    LoadTypesFromAssembly(context);
                } 

                Debug.Assert(!context.KnownAssemblies.ContainsKey(context.CurrentAssembly), "This assembly must not be present in the list of known assemblies");
                context.KnownAssemblies.Add(context.CurrentAssembly, false/*ReferencedAssembliesNotLoaded*/);
 
                // When loading assembly from cache, the cache provide the implicit-dependency i.e. cross-reference by it's type to types in other assemblies.
                // In the case where assembly is loaded for the first-time, the loading process ensures that implicitly-dependenent assemblies are loaded 
                foreach (Assembly referencedAssembly in context.AssemblyCacheEntry._referencedAssemblies) 
                {
                    if (!context.KnownAssemblies.ContainsKey(referencedAssembly)) 
                    {
                        // Update the current assembly that we are currently loading
                        context.UpdateCurrentAssembly(referencedAssembly, context.IsAssemblyAlreadyLoadedInCache);
 
                        areAssembliesLoadedFromCache |= InternalLoadAssemblyFromCache(context);
                    } 
                } 

                return areAssembliesLoadedFromCache; 
            }

            /// 
            /// Loads the set of types from the given assembly and adds it to the given list of types 
            /// 
            /// context containing information for loading 
            private static void LoadTypesFromAssembly(LoadingContext context) 
            {
                Debug.Assert(context.AssemblyCacheEntry._typesInAssembly.Count == 0); 

                LoadRelationshipTypes(context);

                // Loop through each type in the assembly and process it 
                foreach (Type type in context.CurrentAssembly.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(DataClasses.EdmTypeAttribute), false)) 
                    {
                        continue;
                    }
 
                    // Load the metadata for this type
                    LoadFromType(type, context); 
                } 
            }
 
            /// 
            /// 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 static EdmType LoadFromType(Type clrType, LoadingContext context)
            { 
                EdmType edmType = null;

                if (clrType.IsNested)
                { 
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NestedClassNotSupported(clrType.FullName, clrType.Assembly.FullName), null));
                    return null; 
                } 

                // Lookup for the specified type in the following structures: 
                // 1) typesInLoading - that describes the (*new*) types loaded so far by the current load-operation.
                // 2) objectItemCollection.Items - describes the types that are already loaded in ObjectItemCollection
                // 3) AssemblyCache - this describes a set of AssemblyCacheEntries containing 1) assembly types and 2) (implicitly) dependent assemblies
                //                    iff type is present in assembly_cache, add it to typesInLoading. 
                if (!clrType.IsGenericType && context.IsTypeAlreadyLoaded(clrType, out edmType))
                { 
                    // Check to make sure the CLR type we got is the same as the given one 
                    if (edmType.ClrType != clrType)
                    { 
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NewTypeConflictsWithExistingType(
                                                    clrType.AssemblyQualifiedName, edmType.ClrType.AssemblyQualifiedName), edmType));
                        return null;
                    } 

                    return edmType; 
                } 

                DataClasses.EdmTypeAttribute[] typeAttributes = (DataClasses.EdmTypeAttribute[])clrType.GetCustomAttributes(typeof(DataClasses.EdmTypeAttribute), false /*inherit*/); 

                // the CLR doesn't allow types to have duplicate/multiple attribute declarations

                if (typeAttributes.Length != 0) 
                {
                    DataClasses.EdmTypeAttribute typeAttribute = typeAttributes[0]; 
                    string cspaceTypeName = String.IsNullOrEmpty(typeAttribute.Name) ? clrType.Name : typeAttribute.Name; 
                    if(String.IsNullOrEmpty(typeAttribute.NamespaceName) && clrType.Namespace == null)
                    { 
                        context.EdmItemError.Add(new EdmItemError(Strings.Validator_TypeHasNoNamespace, edmType));
                        return null;
                    }
 
                    string cspaceNamespaceName = String.IsNullOrEmpty(typeAttribute.NamespaceName) ? clrType.Namespace : typeAttribute.NamespaceName;
 
                    if (typeAttribute.GetType() == typeof(DataClasses.EdmEntityTypeAttribute)) 
                    {
                        edmType = new ClrEntityType(clrType, cspaceNamespaceName, cspaceTypeName); 
                    }
                    else
                    {
                        Debug.Assert(typeAttribute.GetType() == typeof(DataClasses.EdmComplexTypeAttribute), "Invalid type attribute encountered"); 
                        edmType = new ClrComplexType(clrType, cspaceNamespaceName, cspaceTypeName);
                    } 
                } 
                else
                { 
                    return ResolveNonSchemaType(clrType, context);
                }

                // If type is not present in the current assembly, make sure you add the type's assembly in the list of referenced 
                // assembly for the current assembly. But we still have to load the type and all its dependent type
                if (clrType.Assembly != context.CurrentAssembly) 
                { 
                    // 1) Enqueue the "other" assembly for being loaded
                    // 2) Register the "other" assembly as a referenced-assembly of currentAssembly 
                    // 3) and Load the specific type from "other" assembly for resolving the forward reference
                    if (ShouldFilterAssembly(clrType.Assembly.FullName) || !IsSchemaAttributePresent(clrType.Assembly))
                    {
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.MissingAssemblyAttribute( 
                                                        clrType.FullName, clrType.Assembly.FullName), edmType));
                        return null; 
                    } 
                    else
                    { 
                        if (!context.AssemblyCacheEntry._referencedAssemblies.Contains(clrType.Assembly))
                        {
                            context.AssemblyCacheEntry._referencedAssemblies.Add(clrType.Assembly);
                        } 
                    }
                } 
                else 
                {
                    Debug.Assert(!context.AssemblyCacheEntry.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
                    context.AssemblyCacheEntry._typesInAssembly.Add(edmType);
                }
 
                // Add this to the known type map so we won't try to load it again
                context.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))
                    { 
                        edmType.BaseType = LoadFromType(clrType.BaseType, context);
                    } 
 
                    // Load the properties for this type
                    LoadPropertiesFromType((StructuralType)edmType, context); 
                }

                return edmType;
            } 

            ///  
            /// Load all the property metadata of the given type 
            /// 
            /// The type where properties are loaded 
            /// 
            private static void LoadPropertiesFromType(StructuralType structuralType, LoadingContext context)
            {
                // 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(BindingFlags.DeclaredOnly | 
                                                                                 BindingFlags.Instance | 
                                                                                 BindingFlags.Public |
                                                                                 BindingFlags.NonPublic); 

                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(DataClasses.EdmRelationshipNavigationPropertyAttribute), false))
                    {
                        SaveNavigationProperty(structuralType, property, context);
                    } 
                    else if (property.IsDefined(typeof(DataClasses.EdmScalarPropertyAttribute), false))
                    { 
                        newMember = LoadScalarProperty(property, context, out isEntityKeyProperty); 
                    }
                    else if (property.IsDefined(typeof(DataClasses.EdmComplexPropertyAttribute), false)) 
                    {
                        newMember = LoadComplexTypeProperty(property, context);
                    }
 
                    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); 
                    }
                } 
            } 

            ///  
            /// Loads metadata for the navigation properties
            /// 
            /// 
            ///  
            /// 
            ///  
            private static void SaveNavigationProperty(StructuralType declaringType, PropertyInfo property, LoadingContext context) 
            {
                Debug.Assert(property.IsDefined(typeof(DataClasses.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 = property.GetCustomAttributes(typeof(DataClasses.EdmRelationshipNavigationPropertyAttribute), false);
 
                Debug.Assert(relationshipPropertyAttributes.Length == 1, "There should be exactly one property for every navigation property"); 

                // Load the property type and create a new property object 
                EdmType propertyType = LoadFromType(property.PropertyType, context);

                // The only valid return types from navigation properties are:
                //     (1) EntityType 
                //     (2) CollectionType containing valid EntityType
 
                // If LoadFromType returned null, 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.
                if (propertyType == null || !(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. 
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_InvalidNavPropReturnType(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null)); 
                }
                else 
                {
                    // 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
                    DataClasses.EdmRelationshipNavigationPropertyAttribute attribute = (DataClasses.EdmRelationshipNavigationPropertyAttribute)relationshipPropertyAttributes[0]; 
 
                    context.UnresolvedNavigationProperties.Add(new NavigationPropertyInfo(declaringType, property, propertyType, attribute));
                } 
            }

            private static void ResolveNavigationProperties(LoadingContext context)
            { 
                foreach (NavigationPropertyInfo info in context.UnresolvedNavigationProperties)
                { 
                    info.ResolveNavigationProperty(context); 
                }
            } 

            /// 
            /// Load the property with scalar property attribute
            ///  
            /// 
            ///  
            ///  
            /// 
            private static EdmMember LoadScalarProperty(PropertyInfo property, LoadingContext context, out bool isEntityKeyProperty) 
            {
                Debug.Assert(property.IsDefined(typeof(DataClasses.EdmScalarPropertyAttribute), false), "The property must have a scalar attribute");
                EdmMember member = null;
                isEntityKeyProperty = false; 

                // Load the property type and create a new property object 
                EdmType propertyType = LoadFromType(property.PropertyType, context); 

                // 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 (propertyType == null || propertyType.BuiltInTypeKind != BuiltInTypeKind.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
                    context.EdmItemError.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(DataClasses.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 = ((DataClasses.EdmScalarPropertyAttribute)attrs[0]).EntityKeyProperty;
                    bool isNullable = ((DataClasses.EdmScalarPropertyAttribute)attrs[0]).IsNullable; 
 
                    member = new EdmProperty(property.Name,
                        TypeUsage.Create(propertyType, new FacetValues { Nullable = isNullable }), 
                        property);

                }
                return member; 
            }
 
            ///  
            /// Load the property with complex type property attribute
            ///  
            /// 
            /// 
            /// 
            private static EdmMember LoadComplexTypeProperty(PropertyInfo property, LoadingContext context) 
            {
                // Load the property type and create a new property object 
                EdmType propertyType = LoadFromType(property.PropertyType, context); 

                // 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 (propertyType == null || 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
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ComplexPropertyNotComplex(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null)); 
                } 
                else
                { 
                    EdmProperty newProperty = new EdmProperty(property.Name,
                        TypeUsage.Create(propertyType, new FacetValues { Nullable = false }),
                        property);
 
                    return newProperty;
                } 
 
                return null;
            } 

            /// 
            /// Resolves the given non-schematized type by looking at what kind of type this is.  Non-schematized types include
            /// collection types, nullable types, reference types, and primitive types. 
            /// 
            /// The non-schematized CLR type that is being resolved 
            /// A dictionary of types that we are currently loading 
            /// An EdmType object representing the type, null if we can't resolve it
            private static EdmType ResolveNonSchemaType(Type clrType, LoadingContext context) 
            {
                // 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 = LoadFromType(clrType.GetGenericArguments()[0], context); 
                    if (elementType == null)
                    {
                        // return null and let the caller deal with the error handling
                        return null; 
                    }
 
                    // Create the collection or reference type object or just a simple value that was wrapped inside a Nullable 
                    if (genericType == typeof(Nullable<>))
                    { 
                        // In here, the Nullable<> is unwrapped.  In CDM, nullability is on a per property basis, there shouldn't be
                        // the notion of a nullable type
                        return elementType;
                    } 
                    else if (genericType == typeof(DataClasses.EntityReference<>))
                    { 
                        // EntityReference is IEnumerable, so we need to detect this condition and return here. 
                        // We don't support EntityReference in this scenario, so just fail.
                        return null; 
                    }
                    else if (typeof(IEnumerable).IsAssignableFrom(clrType))
                    {
                        EntityType entityType = elementType as EntityType; 
                        if (entityType == null)
                        { 
                            // return null and let the caller deal with the error handling 
                            return null;
                        } 
                        return entityType.GetCollectionType();
                    }
                }
 
                // For primitive types, look in clr provider manifest
                PrimitiveType primitiveType; 
                if (ClrProviderManifest.Instance.TryGetPrimitiveType(clrType, out primitiveType)) 
                {
                    return primitiveType; 
                }

                return null;
            } 

            ///  
            /// This method loads all the relationship type that this entity takes part in 
            /// 
            ///  
            /// 
            private static void LoadRelationshipTypes(LoadingContext context)
            {
                foreach (System.Data.Objects.DataClasses.EdmRelationshipAttribute roleAttribute in context.CurrentAssembly.GetCustomAttributes(typeof(System.Data.Objects.DataClasses.EdmRelationshipAttribute), false /*inherit*/)) 
                {
                    // Check if there is an entry already with this name 
                    if (TryFindNullParametersInRelationshipAttribute(roleAttribute, context)) 
                    {
                        // 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) 
                    {
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.SameRoleNameOnRelationshipAttribute(roleAttribute.RelationshipName, roleAttribute.Role2Name), 
                                   null));
                        errorEncountered = true;
                    }
 
                    // Make sure the clr type specified are the same
                    EntityType type1; 
                    if (!TryGetRelationshipEndEntityType(context, roleAttribute.Role1Type, out type1)) 
                    {
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(roleAttribute.RelationshipName, roleAttribute.Role1Name, roleAttribute.Role1Type), 
                                   null));
                        errorEncountered = true;
                    }
 
                    EntityType type2;
                    if (!TryGetRelationshipEndEntityType(context, roleAttribute.Role2Type, out type2)) 
                    { 
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(roleAttribute.RelationshipName, roleAttribute.Role2Name, roleAttribute.Role2Type),
                                   null)); 
                        errorEncountered = true;
                    }

                    if (!errorEncountered) 
                    {
                        AssociationType associationType = new AssociationType(roleAttribute.RelationshipName, roleAttribute.RelationshipNamespaceName, DataSpace.OSpace); 
                        associationType.AddKeyMember(new AssociationEndMember(roleAttribute.Role1Name, type1.GetReferenceType(), roleAttribute.Role1Multiplicity)); 
                        associationType.AddKeyMember(new AssociationEndMember(roleAttribute.Role2Name, type2.GetReferenceType(), roleAttribute.Role2Multiplicity));
                        context.TypesInLoading.Add(associationType.FullName, associationType); 

                        // get assembly entry and add association type to the list of types in the assembly
                        Debug.Assert(!context.AssemblyCacheEntry.ContainsType(associationType.FullName), "Relationship type must not be present in the list of types");
                        context.AssemblyCacheEntry._typesInAssembly.Add(associationType); 
                    }
                } 
            } 

            private static bool TryFindNullParametersInRelationshipAttribute(System.Data.Objects.DataClasses.EdmRelationshipAttribute roleAttribute, LoadingContext context) 
            {
                if (roleAttribute.RelationshipName == null)
                {
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullRelationshipNameforEdmRelationshipAttribute(context.CurrentAssembly.FullName), null)); 
                    return true;
                } 
 
                bool nullsFound = false;
 
                if (roleAttribute.RelationshipNamespaceName == null)
                {
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                        "RelationshipNamespaceName", roleAttribute.RelationshipName), null)); 
                    nullsFound = true;
                } 
 
                if (roleAttribute.Role1Name == null)
                { 
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                        "Role1Name", roleAttribute.RelationshipName), null));
                    nullsFound = true;
                } 

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

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

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

                return nullsFound; 
            }

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

                EdmType edmType = LoadFromType(type, context);
                if (edmType == null || !Helper.IsEntityType(edmType))
                { 
                    entityType = null;
                    return false; 
                } 
                entityType = (EntityType)edmType;
                return true; 
            }

            #endregion
        } 

        #endregion 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections; 
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Serialization;
using System.Xml; 
using System.Xml.Schema;
using System.Data.Common.Utils; 
using System.Diagnostics; 
using System.Collections.ObjectModel;
using System.Threading; 
// Using an alias for this because a lot of names in this namespace conflicts with names in metadata
using DataClasses = System.Data.Objects.DataClasses;
using System.Globalization;
using System.Data.Entity; 
using System.Data.Common;
 
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
    ///  
    public sealed partial class ObjectItemCollection : ItemCollection
    { 
        #region PrivateNestedClass 
        private class AssemblyCacheEntry
        { 
            #region Fields
            private readonly List _typesInAssembly;       // types in "this" assembly
            private readonly List _referencedAssemblies; // other assemblies referenced by "this" assembly
 
            private static object _assemblyCacheLock = new object();
 
            //List of assemblies having view gen attribute. We cache these things if we discover 
            //these assemblies while looking for O-space metadata.
            private static IList s_viewGenAssemblies = new ThreadSafeList(); 

            #endregion

            #region Nested classes 

            private class LoadingContext 
            { 
                #region Fields
                // all the types that we encountered while loading - this may contain types from various assemblies 
                private readonly Dictionary _typesInLoading;

                // list of errors encountered during loading
                private readonly List _errors; 

                // list of unresolved navigation properties 
                private readonly List _unresolvedNavigationProperties = new List(); 

                // keep the list of new assemblies that got loaded in this load assembly call. The region why we need to keep a seperate 
                // assembly is that if we need to keep track of errors, and if there are no errors, then only add the list of assemblies
                // to the global cache. Hence global cache is never polluted with invalid assemblies
                private readonly Dictionary _listOfAssembliesLoaded = new Dictionary();
 
                // Current assembly whose type we are loading
                private Assembly _currentAssembly; 
 
                // Assembly Cache Entry corresponding to the current assembly
                private AssemblyCacheEntry _currentCacheEntry; 

                // Indicates if this assembly is already loaded in the cache
                private bool _isAssemblyLoadedFromGlobalCache;
 
                // Global Assembly Cache
                private readonly static Dictionary s_globalAssemblyCache = new Dictionary(); 
 
                // List of known assemblies - this list is initially passed by the caller and we keep adding to it, as and when we load
                // an assembly 
                private readonly Dictionary _knownAssemblies;

                #endregion
 
                #region Constructor
                internal LoadingContext(Assembly assembly, Dictionary knownAssemblies) 
                { 
                    _typesInLoading = new Dictionary(StringComparer.Ordinal);
                    _errors = new List(); 
                    _knownAssemblies = knownAssemblies;

                    UpdateCurrentAssembly(assembly, false/*mustAssemblyBeAlreadyLoaded*/);
                } 
                #endregion
 
                #region Properties 

                internal Dictionary TypesInLoading { get { return _typesInLoading; } } 

                internal List EdmItemError { get { return _errors; } }

                internal List UnresolvedNavigationProperties { get { return _unresolvedNavigationProperties; } } 

                internal Assembly CurrentAssembly { get { return _currentAssembly; } } 
 
                internal AssemblyCacheEntry AssemblyCacheEntry { get { return _currentCacheEntry; } }
 
                internal bool IsAssemblyAlreadyLoadedInCache { get { return _isAssemblyLoadedFromGlobalCache; } }

                internal Dictionary KnownAssemblies { get { return _knownAssemblies; } }
 
                #endregion
 
                #region Methods 

                ///  
                /// Check to see if the type is already loaded - either in the typesInLoading, or ObjectItemCollection or
                /// in the global cache
                /// 
                ///  
                /// 
                ///  
                internal bool IsTypeAlreadyLoaded(Type clrType, out EdmType edmType) 
                {
                    edmType = null; 
                    bool isPresentInAssemblyCache = false;
                    bool isPresentInTypesInLoading = false;

                    if (!clrType.IsGenericType && ((isPresentInTypesInLoading = TypesInLoading.TryGetValue(clrType.FullName, out edmType)) || 
                                                   (isPresentInAssemblyCache = IsTypeAlreadyInCache(clrType, out edmType))))
                    { 
                        // If the type is primitive type, just return the type 
                        if (!Helper.IsPrimitiveType(edmType))
                        { 
                            Debug.Assert(!ShouldFilterAssembly(clrType.Assembly.FullName), "Since the type is already loaded, the assembly must have a schema attribute");
                            Debug.Assert(ObjectItemCollection.IsSchemaAttributePresent(clrType.Assembly), "Since the type is already loaded, the assembly must have a schema attribute");

                            // If the type is not present in the current assembly, make sure you add the type's assembly 
                            // as one of the referenced assemblies
                            if (clrType.Assembly != _currentAssembly) 
                            { 
                                if (!_currentCacheEntry._referencedAssemblies.Contains(clrType.Assembly))
                                { 
                                    _currentCacheEntry._referencedAssemblies.Add(clrType.Assembly);
                                }
                            }
                            // If the base type BT1 of a type T1 is present in another assembly, we just load the base type from that assembly and add 
                            // that assembly to the list of referenced assemblies. When you come to loading the referenced assembly, the type BT1
                            // is already present in TypeInLoading, and hence we need to add it to the list of types in assembly 
                            else if (isPresentInTypesInLoading && !_currentCacheEntry.ContainsType(edmType.Identity)) 
                            {
                                _currentCacheEntry._typesInAssembly.Add(edmType); 
                            }

                            // If the type was loaded from the global cache, then we need to find if this assembly is already loaded. If yes,
                            // then we don't need to add that type in typesInLoading 
                            if (isPresentInAssemblyCache && !KnownAssemblies.ContainsKey(clrType.Assembly))
                            { 
                                TypesInLoading.Add(clrType.FullName, edmType); 
                            }
                        } 
                    }

                    return (edmType != null);
                } 

                ///  
                /// Returns if the types is already loaded in the cache 
                /// 
                ///  
                /// 
                /// 
                private bool IsTypeAlreadyInCache(Type clrType, out EdmType edmType)
                { 
                    AssemblyCacheEntry cacheEntry;
                    edmType = null; 
 
                    Debug.Assert(!_typesInLoading.ContainsKey(clrType.FullName), "This should be called only after looking in typesInLoading");
                    Debug.Assert(clrType.Assembly != _currentAssembly || !_currentCacheEntry.ContainsType(clrType.FullName), "The type must never be present in the current assembly list"); 

                    if (clrType.Assembly == _currentAssembly)
                    {
                        return false; 
                    }
 
                    if (s_globalAssemblyCache.TryGetValue(clrType.Assembly, out cacheEntry)) 
                    {
                        return cacheEntry.TryGetEdmType(clrType.FullName, out edmType); 
                    }
                    else if (_listOfAssembliesLoaded.TryGetValue(clrType.Assembly, out cacheEntry))
                    {
                        return cacheEntry.TryGetEdmType(clrType.FullName, out edmType); 
                    }
 
                    return false; 
                }
 
                /// 
                /// Update the current assembly for the loading context. If the second parameter is true, assert that
                /// the assembly must be present in the global cache. this is to make sure if a assembly was already
                /// present in the global cache, all its dependent assemblies must also be present in the global cache 
                /// This method checks if the given assembly is present in the global cache, if yes, its loads from there
                /// otherwise creates a new AssemblyCacheEntry for this assembly. 
                /// Also it adds the earlier current assembly, into its local cache. The reason for doing this is that until 
                /// we have loaded all the assemblies for this context and made sure that there are no errors, then we need
                /// to update the global cache 
                /// 
                /// 
                /// 
                internal void UpdateCurrentAssembly(Assembly assembly, bool mustAssemblyBeAlreadyLoaded) 
                {
                    Debug.Assert(assembly != null, "Current Assembly can't be set to null"); 
                    Debug.Assert(!mustAssemblyBeAlreadyLoaded || s_globalAssemblyCache.ContainsKey(assembly), "The assembly must be loaded in the cache"); 

                    // Update the current assembly 
                    _currentAssembly = assembly;

                    if (mustAssemblyBeAlreadyLoaded)
                    { 
                        // check if the assembly is already loaded in the cache
                        _currentCacheEntry = s_globalAssemblyCache[assembly]; 
                        _isAssemblyLoadedFromGlobalCache = true; 
                    }
                    else if (s_globalAssemblyCache.TryGetValue(assembly, out _currentCacheEntry)) 
                    {
                        _isAssemblyLoadedFromGlobalCache = true;
                    }
                    // If the assemblies have circular dependencies then the assembly might have been added in the local cache 
                    else
                    { 
                        if (!_listOfAssembliesLoaded.TryGetValue(assembly, out _currentCacheEntry)) 
                        {
                            _currentCacheEntry = new AssemblyCacheEntry(); 
                            _listOfAssembliesLoaded.Add(_currentAssembly, _currentCacheEntry);
                        }
                        _isAssemblyLoadedFromGlobalCache = false;
                    } 
                }
 
                // Add all assemblies to the global cache if there are no errors 
                internal void UpdateCacheWithAssembliesLoaded()
                { 
                    if (_errors.Count == 0)
                    {
                        foreach (KeyValuePair entry in _listOfAssembliesLoaded)
                        { 
                            // Add all the assemblies from the loading context to the global cache
                            s_globalAssemblyCache.Add(entry.Key, entry.Value); 
                        } 

                        // Remove all entries from transient cache 
                        _listOfAssembliesLoaded.Clear();
                    }
                }
                #endregion 
            }
            private class NavigationPropertyInfo 
            { 
                private StructuralType _declaringType;
                private PropertyInfo _propertyInfo; 
                private EdmType _propertyType;
                private DataClasses.EdmRelationshipNavigationPropertyAttribute _attribute;

                internal NavigationPropertyInfo( 
                        StructuralType declaringType,
                        PropertyInfo propertyInfo, 
                        EdmType propertyType, 
                        DataClasses.EdmRelationshipNavigationPropertyAttribute attribute)
                { 
                    _declaringType = declaringType;
                    _propertyInfo = propertyInfo;
                    _propertyType = propertyType;
                    _attribute = attribute; 
                }
 
                internal void ResolveNavigationProperty(LoadingContext context) 
                {
                    EdmMember member = null; 
                    EdmType type;
                    if (context.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
                            { 
                                context.EdmItemError.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) 
                            {
                                context.EdmItemError.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 
                    {
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RelationshipNameInNavigationPropertyNotValid( 
                                                    _propertyInfo.Name, _propertyInfo.DeclaringType.FullName, _attribute.RelationshipName), _declaringType));
                    }

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

            }

 
            #endregion
 
            #region Constructor 
            public AssemblyCacheEntry()
            { 
                _typesInAssembly = new List();
                _referencedAssemblies = new List();
            }
            #endregion 

            #region Internal Methods (Entry points to the Cache) 
 
            internal static IList ViewGenerationAssemblies
            { 
                get
                {
                    return s_viewGenAssemblies;
                } 
            }
 
            internal static void LoadAssemblyFromCache(Assembly assembly, bool loadReferencedAssemblies, 
                Dictionary knownAssemblies, out Dictionary typesInLoading, out List errors)
            { 
                Debug.Assert(!ShouldFilterAssembly(assembly.FullName), "LoadAssemblyFromCache should be called on assembly having non-reserved public key token");
                typesInLoading = null;
                errors = null;
 
                lock (_assemblyCacheLock)
                { 
                    // This function loads all the types from the given assembly and the dependent assemblies. We only try to load assemblies 
                    // that are requried for type closure. This is different from referenced asssemblies. If the assembly is already loaded,
                    // then the assembly is just copied from the cache 
                    LoadingContext context = new LoadingContext(assembly, knownAssemblies);

                    // Loads the current assembly and all the dependent assemblies (required for type closure)
                    if (!context.KnownAssemblies.ContainsKey(context.CurrentAssembly) && IsSchemaAttributePresent(context.CurrentAssembly)) 
                    {
                        InternalLoadAssemblyFromCache(context); 
                    } 

                    if (loadReferencedAssemblies) 
                    {
                        InternalLoadAllReferencedAssemblies(context);
                    }
 
                    // resolve navigation properties that showed up
                    // before the relationships that they use showed up 
                    ResolveNavigationProperties(context); 

                    // do the validation for the all the new types 
                    // Now, perform validation on all the new types
                    EdmValidator validator = new EdmValidator();
                    validator.SkipReadOnlyItems = true;
                    validator.Validate(context.TypesInLoading.Values, context.EdmItemError); 

                    // Update the global cache if there are no errors 
                    context.UpdateCacheWithAssembliesLoaded(); 

                    // Update the out parameters once you are done with loading 
                    typesInLoading = context.TypesInLoading;
                    errors = context.EdmItemError;

                    if (typesInLoading != null && typesInLoading.Count > 0) 
                    {
                        foreach (EdmType edmType in typesInLoading.Values) 
                        { 
                            edmType.SetReadOnly();
                        } 
                    }
                }
            }
 
            internal static Assembly SafeLoadReferencedAssembly(string assemblyFullName)
            { 
                Assembly referencedAssembly = null; 

                try 
                {
                    referencedAssembly = Assembly.Load(assemblyFullName);
                }
                catch (System.IO.FileNotFoundException) 
                {
                    // See 552932: ObjectItemCollection: fails on referenced asseblies that are not available 
                } 

                return referencedAssembly; 
            }
            #endregion

            #region Private Methods 
            private bool TryGetEdmType(string typeName, out EdmType edmType)
            { 
                edmType = null; 
                foreach (EdmType loadedEdmType in this._typesInAssembly)
                { 
                    if (loadedEdmType.Identity == typeName)
                    {
                        edmType = loadedEdmType;
                        break; 
                    }
                } 
                return (edmType != null); 
            }
 
            private bool ContainsType(string typeName)
            {
                EdmType edmType = null;
                return TryGetEdmType(typeName, out edmType); 
            }
 
            private static void InternalLoadAllReferencedAssemblies(LoadingContext context) 
            {
                // We will traverse through all the statically linked assemblies and their dependencies. 
                // Only assemblies with the EdmSchemaAttribute will be loaded and rest will be ignored

                // Even if the schema attribute is missing, we should still check all the dependent assemblies
                // any of the dependent assemblies can have the schema attribute 

                // After the given assembly has been loaded, check on the flag in _knownAssemblies to see if it has already 
                // been recursively loaded. The flag can be true if it was already loaded before this function was called 
                foreach (AssemblyName asmName in context.CurrentAssembly.GetReferencedAssemblies())
                { 
                    string assemblyFullName = asmName.FullName;
                    if (!ShouldFilterAssembly(assemblyFullName))
                    {
                        // filter out "known" assemblies to prevent unnecessary loading 
                        EntityBid.Trace(" loadededAssembly='%ls'\n", assemblyFullName);
 
                        Assembly referencedAssembly = SafeLoadReferencedAssembly(assemblyFullName); 
                        if (referencedAssembly == null)
                        { 
                            continue;
                        }

                        // Mark the assembly as known assembly, and since we are loading all the referenced assemblies, 
                        // mark the value to the true
                        context.UpdateCurrentAssembly(referencedAssembly, false/*mustAlreadyBeLoaded*/); 
 
                        // Check if the assembly is already loaded
                        bool areReferencedAssembliesLoaded; 
                        if (context.KnownAssemblies.TryGetValue(referencedAssembly, out areReferencedAssembliesLoaded))
                        {
                            // If all the referenced assemblies are already loaded, don't need to do anything
                            if (areReferencedAssembliesLoaded) 
                            {
                                continue; 
                            } 
                        }
                        // Load this assembly if the schema attrbute is present 
                        else if (ObjectItemCollection.IsSchemaAttributePresent(referencedAssembly))
                        {
                            InternalLoadAssemblyFromCache(context);
                        } 

                        // We need to add this assembly to the list of known assemblies before we start 
                        // analyzing the referenced assemblies, since there could be circular reference 
                        // and we need to detect that and break the loop
                        context.KnownAssemblies[referencedAssembly] = true; 
                        InternalLoadAllReferencedAssemblies(context);
                    }
                }
            } 

            ///  
            /// 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
            private static bool InternalLoadAssemblyFromCache(LoadingContext context)
            { 
                Debug.Assert(!ShouldFilterAssembly(context.CurrentAssembly.FullName), "LoadAssemblyFromCache should be called on assembly having non-reserved public key token");
                Debug.Assert(IsSchemaAttributePresent(context.CurrentAssembly), "LoadAssembly shouldn't be called with assembly having no schema attribute"); 
                Debug.Assert(!context.KnownAssemblies.ContainsKey(context.CurrentAssembly), "InternalLoadAssemblyFromCache: This assembly must not be present in the list of known assemblies"); 

                bool areAssembliesLoadedFromCache = context.IsAssemblyAlreadyLoadedInCache; 

                // Check if the assembly has been loaded in the cache then:
                // 1) Add EdmTypes described in ----semblyCacheEntry.TypesInAssembly
                // 2) Add Assemblies described by AssemblyCacheEntry.ReferenceAssembly, check to make sure that it's not already loaded in ObjectItemCollection 
                if (context.IsAssemblyAlreadyLoadedInCache)
                { 
                    foreach (EdmType type in context.AssemblyCacheEntry._typesInAssembly) 
                    {
                        if (!context.TypesInLoading.ContainsKey(type.Identity)) 
                        {
                            context.TypesInLoading.Add(type.Identity, type);
                        }
                    } 
                }
                else 
                { 
                    LoadTypesFromAssembly(context);
                } 

                Debug.Assert(!context.KnownAssemblies.ContainsKey(context.CurrentAssembly), "This assembly must not be present in the list of known assemblies");
                context.KnownAssemblies.Add(context.CurrentAssembly, false/*ReferencedAssembliesNotLoaded*/);
 
                // When loading assembly from cache, the cache provide the implicit-dependency i.e. cross-reference by it's type to types in other assemblies.
                // In the case where assembly is loaded for the first-time, the loading process ensures that implicitly-dependenent assemblies are loaded 
                foreach (Assembly referencedAssembly in context.AssemblyCacheEntry._referencedAssemblies) 
                {
                    if (!context.KnownAssemblies.ContainsKey(referencedAssembly)) 
                    {
                        // Update the current assembly that we are currently loading
                        context.UpdateCurrentAssembly(referencedAssembly, context.IsAssemblyAlreadyLoadedInCache);
 
                        areAssembliesLoadedFromCache |= InternalLoadAssemblyFromCache(context);
                    } 
                } 

                return areAssembliesLoadedFromCache; 
            }

            /// 
            /// Loads the set of types from the given assembly and adds it to the given list of types 
            /// 
            /// context containing information for loading 
            private static void LoadTypesFromAssembly(LoadingContext context) 
            {
                Debug.Assert(context.AssemblyCacheEntry._typesInAssembly.Count == 0); 

                LoadRelationshipTypes(context);

                // Loop through each type in the assembly and process it 
                foreach (Type type in context.CurrentAssembly.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(DataClasses.EdmTypeAttribute), false)) 
                    {
                        continue;
                    }
 
                    // Load the metadata for this type
                    LoadFromType(type, context); 
                } 
            }
 
            /// 
            /// 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 static EdmType LoadFromType(Type clrType, LoadingContext context)
            { 
                EdmType edmType = null;

                if (clrType.IsNested)
                { 
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NestedClassNotSupported(clrType.FullName, clrType.Assembly.FullName), null));
                    return null; 
                } 

                // Lookup for the specified type in the following structures: 
                // 1) typesInLoading - that describes the (*new*) types loaded so far by the current load-operation.
                // 2) objectItemCollection.Items - describes the types that are already loaded in ObjectItemCollection
                // 3) AssemblyCache - this describes a set of AssemblyCacheEntries containing 1) assembly types and 2) (implicitly) dependent assemblies
                //                    iff type is present in assembly_cache, add it to typesInLoading. 
                if (!clrType.IsGenericType && context.IsTypeAlreadyLoaded(clrType, out edmType))
                { 
                    // Check to make sure the CLR type we got is the same as the given one 
                    if (edmType.ClrType != clrType)
                    { 
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NewTypeConflictsWithExistingType(
                                                    clrType.AssemblyQualifiedName, edmType.ClrType.AssemblyQualifiedName), edmType));
                        return null;
                    } 

                    return edmType; 
                } 

                DataClasses.EdmTypeAttribute[] typeAttributes = (DataClasses.EdmTypeAttribute[])clrType.GetCustomAttributes(typeof(DataClasses.EdmTypeAttribute), false /*inherit*/); 

                // the CLR doesn't allow types to have duplicate/multiple attribute declarations

                if (typeAttributes.Length != 0) 
                {
                    DataClasses.EdmTypeAttribute typeAttribute = typeAttributes[0]; 
                    string cspaceTypeName = String.IsNullOrEmpty(typeAttribute.Name) ? clrType.Name : typeAttribute.Name; 
                    if(String.IsNullOrEmpty(typeAttribute.NamespaceName) && clrType.Namespace == null)
                    { 
                        context.EdmItemError.Add(new EdmItemError(Strings.Validator_TypeHasNoNamespace, edmType));
                        return null;
                    }
 
                    string cspaceNamespaceName = String.IsNullOrEmpty(typeAttribute.NamespaceName) ? clrType.Namespace : typeAttribute.NamespaceName;
 
                    if (typeAttribute.GetType() == typeof(DataClasses.EdmEntityTypeAttribute)) 
                    {
                        edmType = new ClrEntityType(clrType, cspaceNamespaceName, cspaceTypeName); 
                    }
                    else
                    {
                        Debug.Assert(typeAttribute.GetType() == typeof(DataClasses.EdmComplexTypeAttribute), "Invalid type attribute encountered"); 
                        edmType = new ClrComplexType(clrType, cspaceNamespaceName, cspaceTypeName);
                    } 
                } 
                else
                { 
                    return ResolveNonSchemaType(clrType, context);
                }

                // If type is not present in the current assembly, make sure you add the type's assembly in the list of referenced 
                // assembly for the current assembly. But we still have to load the type and all its dependent type
                if (clrType.Assembly != context.CurrentAssembly) 
                { 
                    // 1) Enqueue the "other" assembly for being loaded
                    // 2) Register the "other" assembly as a referenced-assembly of currentAssembly 
                    // 3) and Load the specific type from "other" assembly for resolving the forward reference
                    if (ShouldFilterAssembly(clrType.Assembly.FullName) || !IsSchemaAttributePresent(clrType.Assembly))
                    {
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.MissingAssemblyAttribute( 
                                                        clrType.FullName, clrType.Assembly.FullName), edmType));
                        return null; 
                    } 
                    else
                    { 
                        if (!context.AssemblyCacheEntry._referencedAssemblies.Contains(clrType.Assembly))
                        {
                            context.AssemblyCacheEntry._referencedAssemblies.Add(clrType.Assembly);
                        } 
                    }
                } 
                else 
                {
                    Debug.Assert(!context.AssemblyCacheEntry.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
                    context.AssemblyCacheEntry._typesInAssembly.Add(edmType);
                }
 
                // Add this to the known type map so we won't try to load it again
                context.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))
                    { 
                        edmType.BaseType = LoadFromType(clrType.BaseType, context);
                    } 
 
                    // Load the properties for this type
                    LoadPropertiesFromType((StructuralType)edmType, context); 
                }

                return edmType;
            } 

            ///  
            /// Load all the property metadata of the given type 
            /// 
            /// The type where properties are loaded 
            /// 
            private static void LoadPropertiesFromType(StructuralType structuralType, LoadingContext context)
            {
                // 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(BindingFlags.DeclaredOnly | 
                                                                                 BindingFlags.Instance | 
                                                                                 BindingFlags.Public |
                                                                                 BindingFlags.NonPublic); 

                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(DataClasses.EdmRelationshipNavigationPropertyAttribute), false))
                    {
                        SaveNavigationProperty(structuralType, property, context);
                    } 
                    else if (property.IsDefined(typeof(DataClasses.EdmScalarPropertyAttribute), false))
                    { 
                        newMember = LoadScalarProperty(property, context, out isEntityKeyProperty); 
                    }
                    else if (property.IsDefined(typeof(DataClasses.EdmComplexPropertyAttribute), false)) 
                    {
                        newMember = LoadComplexTypeProperty(property, context);
                    }
 
                    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); 
                    }
                } 
            } 

            ///  
            /// Loads metadata for the navigation properties
            /// 
            /// 
            ///  
            /// 
            ///  
            private static void SaveNavigationProperty(StructuralType declaringType, PropertyInfo property, LoadingContext context) 
            {
                Debug.Assert(property.IsDefined(typeof(DataClasses.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 = property.GetCustomAttributes(typeof(DataClasses.EdmRelationshipNavigationPropertyAttribute), false);
 
                Debug.Assert(relationshipPropertyAttributes.Length == 1, "There should be exactly one property for every navigation property"); 

                // Load the property type and create a new property object 
                EdmType propertyType = LoadFromType(property.PropertyType, context);

                // The only valid return types from navigation properties are:
                //     (1) EntityType 
                //     (2) CollectionType containing valid EntityType
 
                // If LoadFromType returned null, 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.
                if (propertyType == null || !(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. 
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_InvalidNavPropReturnType(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null)); 
                }
                else 
                {
                    // 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
                    DataClasses.EdmRelationshipNavigationPropertyAttribute attribute = (DataClasses.EdmRelationshipNavigationPropertyAttribute)relationshipPropertyAttributes[0]; 
 
                    context.UnresolvedNavigationProperties.Add(new NavigationPropertyInfo(declaringType, property, propertyType, attribute));
                } 
            }

            private static void ResolveNavigationProperties(LoadingContext context)
            { 
                foreach (NavigationPropertyInfo info in context.UnresolvedNavigationProperties)
                { 
                    info.ResolveNavigationProperty(context); 
                }
            } 

            /// 
            /// Load the property with scalar property attribute
            ///  
            /// 
            ///  
            ///  
            /// 
            private static EdmMember LoadScalarProperty(PropertyInfo property, LoadingContext context, out bool isEntityKeyProperty) 
            {
                Debug.Assert(property.IsDefined(typeof(DataClasses.EdmScalarPropertyAttribute), false), "The property must have a scalar attribute");
                EdmMember member = null;
                isEntityKeyProperty = false; 

                // Load the property type and create a new property object 
                EdmType propertyType = LoadFromType(property.PropertyType, context); 

                // 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 (propertyType == null || propertyType.BuiltInTypeKind != BuiltInTypeKind.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
                    context.EdmItemError.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(DataClasses.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 = ((DataClasses.EdmScalarPropertyAttribute)attrs[0]).EntityKeyProperty;
                    bool isNullable = ((DataClasses.EdmScalarPropertyAttribute)attrs[0]).IsNullable; 
 
                    member = new EdmProperty(property.Name,
                        TypeUsage.Create(propertyType, new FacetValues { Nullable = isNullable }), 
                        property);

                }
                return member; 
            }
 
            ///  
            /// Load the property with complex type property attribute
            ///  
            /// 
            /// 
            /// 
            private static EdmMember LoadComplexTypeProperty(PropertyInfo property, LoadingContext context) 
            {
                // Load the property type and create a new property object 
                EdmType propertyType = LoadFromType(property.PropertyType, context); 

                // 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 (propertyType == null || 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
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ComplexPropertyNotComplex(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null)); 
                } 
                else
                { 
                    EdmProperty newProperty = new EdmProperty(property.Name,
                        TypeUsage.Create(propertyType, new FacetValues { Nullable = false }),
                        property);
 
                    return newProperty;
                } 
 
                return null;
            } 

            /// 
            /// Resolves the given non-schematized type by looking at what kind of type this is.  Non-schematized types include
            /// collection types, nullable types, reference types, and primitive types. 
            /// 
            /// The non-schematized CLR type that is being resolved 
            /// A dictionary of types that we are currently loading 
            /// An EdmType object representing the type, null if we can't resolve it
            private static EdmType ResolveNonSchemaType(Type clrType, LoadingContext context) 
            {
                // 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 = LoadFromType(clrType.GetGenericArguments()[0], context); 
                    if (elementType == null)
                    {
                        // return null and let the caller deal with the error handling
                        return null; 
                    }
 
                    // Create the collection or reference type object or just a simple value that was wrapped inside a Nullable 
                    if (genericType == typeof(Nullable<>))
                    { 
                        // In here, the Nullable<> is unwrapped.  In CDM, nullability is on a per property basis, there shouldn't be
                        // the notion of a nullable type
                        return elementType;
                    } 
                    else if (genericType == typeof(DataClasses.EntityReference<>))
                    { 
                        // EntityReference is IEnumerable, so we need to detect this condition and return here. 
                        // We don't support EntityReference in this scenario, so just fail.
                        return null; 
                    }
                    else if (typeof(IEnumerable).IsAssignableFrom(clrType))
                    {
                        EntityType entityType = elementType as EntityType; 
                        if (entityType == null)
                        { 
                            // return null and let the caller deal with the error handling 
                            return null;
                        } 
                        return entityType.GetCollectionType();
                    }
                }
 
                // For primitive types, look in clr provider manifest
                PrimitiveType primitiveType; 
                if (ClrProviderManifest.Instance.TryGetPrimitiveType(clrType, out primitiveType)) 
                {
                    return primitiveType; 
                }

                return null;
            } 

            ///  
            /// This method loads all the relationship type that this entity takes part in 
            /// 
            ///  
            /// 
            private static void LoadRelationshipTypes(LoadingContext context)
            {
                foreach (System.Data.Objects.DataClasses.EdmRelationshipAttribute roleAttribute in context.CurrentAssembly.GetCustomAttributes(typeof(System.Data.Objects.DataClasses.EdmRelationshipAttribute), false /*inherit*/)) 
                {
                    // Check if there is an entry already with this name 
                    if (TryFindNullParametersInRelationshipAttribute(roleAttribute, context)) 
                    {
                        // 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) 
                    {
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.SameRoleNameOnRelationshipAttribute(roleAttribute.RelationshipName, roleAttribute.Role2Name), 
                                   null));
                        errorEncountered = true;
                    }
 
                    // Make sure the clr type specified are the same
                    EntityType type1; 
                    if (!TryGetRelationshipEndEntityType(context, roleAttribute.Role1Type, out type1)) 
                    {
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(roleAttribute.RelationshipName, roleAttribute.Role1Name, roleAttribute.Role1Type), 
                                   null));
                        errorEncountered = true;
                    }
 
                    EntityType type2;
                    if (!TryGetRelationshipEndEntityType(context, roleAttribute.Role2Type, out type2)) 
                    { 
                        context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(roleAttribute.RelationshipName, roleAttribute.Role2Name, roleAttribute.Role2Type),
                                   null)); 
                        errorEncountered = true;
                    }

                    if (!errorEncountered) 
                    {
                        AssociationType associationType = new AssociationType(roleAttribute.RelationshipName, roleAttribute.RelationshipNamespaceName, DataSpace.OSpace); 
                        associationType.AddKeyMember(new AssociationEndMember(roleAttribute.Role1Name, type1.GetReferenceType(), roleAttribute.Role1Multiplicity)); 
                        associationType.AddKeyMember(new AssociationEndMember(roleAttribute.Role2Name, type2.GetReferenceType(), roleAttribute.Role2Multiplicity));
                        context.TypesInLoading.Add(associationType.FullName, associationType); 

                        // get assembly entry and add association type to the list of types in the assembly
                        Debug.Assert(!context.AssemblyCacheEntry.ContainsType(associationType.FullName), "Relationship type must not be present in the list of types");
                        context.AssemblyCacheEntry._typesInAssembly.Add(associationType); 
                    }
                } 
            } 

            private static bool TryFindNullParametersInRelationshipAttribute(System.Data.Objects.DataClasses.EdmRelationshipAttribute roleAttribute, LoadingContext context) 
            {
                if (roleAttribute.RelationshipName == null)
                {
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullRelationshipNameforEdmRelationshipAttribute(context.CurrentAssembly.FullName), null)); 
                    return true;
                } 
 
                bool nullsFound = false;
 
                if (roleAttribute.RelationshipNamespaceName == null)
                {
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                        "RelationshipNamespaceName", roleAttribute.RelationshipName), null)); 
                    nullsFound = true;
                } 
 
                if (roleAttribute.Role1Name == null)
                { 
                    context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
                        "Role1Name", roleAttribute.RelationshipName), null));
                    nullsFound = true;
                } 

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

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

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

                return nullsFound; 
            }

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

                EdmType edmType = LoadFromType(type, context);
                if (edmType == null || !Helper.IsEntityType(edmType))
                { 
                    entityType = null;
                    return false; 
                } 
                entityType = (EntityType)edmType;
                return true; 
            }

            #endregion
        } 

        #endregion 
    } 
}

// 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