ObjectItemConventionAssemblyLoader.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

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

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

 
using System.Reflection;
using System.Linq;
using System.Diagnostics;
using System.Collections.Generic; 
using System.Data.Entity;
using System.Text; 
 
namespace System.Data.Metadata.Edm
{ 
    internal sealed class ObjectItemConventionAssemblyLoader : ObjectItemAssemblyLoader
    {
        // for root entities, entities with no base type, we will additionally look
        // at properties on the clr base hierarchy. 
        private const BindingFlags RootEntityPropertyReflectionBindingFlags = PropertyReflectionBindingFlags & ~BindingFlags.DeclaredOnly | BindingFlags.FlattenHierarchy;
 
        private new MutableAssemblyCacheEntry CacheEntry { get { return (MutableAssemblyCacheEntry)base.CacheEntry; } } 
        private List _referenceResolutions = new List();
 
        internal ObjectItemConventionAssemblyLoader(Assembly assembly, ObjectItemLoadingSessionData sessionData)
            : base(assembly, new MutableAssemblyCacheEntry(), sessionData)
        {
            Debug.Assert(Create == sessionData.ObjectItemAssemblyLoaderFactory, "Why is there a different factory creating this class"); 
            SessionData.RegisterForLevel1PostSessionProcessing(this);
        } 
 
        protected override void LoadTypesFromAssembly()
        { 
            foreach (Type type in SourceAssembly.GetTypes())
            {
                StructuralType cspaceType;
                if (TryGetCSpaceTypeMatch(type, out cspaceType)) 
                {
                    if (type.IsValueType) 
                    { 
                        SessionData.LoadMessageLogger.LogLoadMessage(Strings.Validator_OSpace_Convention_Struct(cspaceType.FullName, type.FullName), cspaceType);
                        continue; 
                    }

                    EdmType ospaceType;
                    if (TryCreateType(type, (StructuralType)cspaceType, out ospaceType)) 
                    {
                        CacheEntry.TypesInAssembly.Add(ospaceType); 
                        // check for duplcates so we don't cause an ArgumentException, 
                        // Mapping will do the actual error for the duplicate type later
                        if (!SessionData.CspaceToOspace.ContainsKey((StructuralType)cspaceType)) 
                        {
                            SessionData.CspaceToOspace.Add((StructuralType)cspaceType, (StructuralType)ospaceType);
                        }
                        else 
                        {
                            // at this point there is already a Clr Type that is structurally matched to this CSpace type, we throw exception 
                            StructuralType previousOSpaceType = SessionData.CspaceToOspace[cspaceType]; 
                            SessionData.EdmItemErrors.Add(
                                new EdmItemError(Strings.Validator_OSpace_Convention_AmbiguousClrType(cspaceType.Name, previousOSpaceType.ClrType.FullName, type.FullName), previousOSpaceType)); 
                        }
                    }
                }
            } 

            if (SessionData.TypesInLoading.Count == 0) 
            { 
                Debug.Assert(CacheEntry.ClosureAssemblies.Count == 0, "How did we get closure assemblies?");
 
                // since we didn't find any types, don't lock into convention based
                SessionData.ObjectItemAssemblyLoaderFactory = null;
            }
        } 

 
        protected override void AddToAssembliesLoaded() 
        {
            SessionData.AssembliesLoaded.Add(SourceAssembly, CacheEntry); 
        }

        private bool TryGetCSpaceTypeMatch(Type type, out StructuralType cspaceType)
        { 
            // brute force try and find a matching name
            KeyValuePair pair; 
            if (SessionData.ConventionCSpaceTypeNames.TryGetValue(type.Name, out pair)) 
            {
                if (pair.Value == 1) 
                {
                    // we found a type match
                    cspaceType = pair.Key;
                    return true; 
                }
                else 
                { 
                    Debug.Assert(pair.Value > 1, "how did we get a negative count of types in the dictionary?");
                    SessionData.EdmItemErrors.Add(new EdmItemError(Strings.Validator_OSpace_Convention_MultipleTypesWithSameName(type.Name), pair.Key)); 
                }
            }

            cspaceType = null; 
            return false;
        } 
 
        private bool TryCreateType(Type type, StructuralType cspaceType, out EdmType newOSpaceType)
        { 
            List referenceResolutionListForCurrentType = new List();
            newOSpaceType = null;
            Debug.Assert(TypesMatchByConvention(type, cspaceType), "The types passed as parameters don't match by convention.");
 
            StructuralType ospaceType;
            if (Helper.IsEntityType(cspaceType)) 
            { 
                ospaceType = new ClrEntityType(type, cspaceType.NamespaceName, cspaceType.Name);
            } 
            else
            {
                Debug.Assert(Helper.IsComplexType(cspaceType), "Invalid type attribute encountered");
                ospaceType = new ClrComplexType(type, cspaceType.NamespaceName, cspaceType.Name); 
            }
 
            if (cspaceType.BaseType != null) 
            {
                if (TypesMatchByConvention(type.BaseType, cspaceType.BaseType)) 
                {
                    TrackClosure(type.BaseType);
                    referenceResolutionListForCurrentType.Add(
                        () => ospaceType.BaseType = ResolveBaseType((StructuralType)cspaceType.BaseType, type)); 
                }
                else 
                { 
                    string message = Strings.Validator_OSpace_Convention_BaseTypeIncompatible(type.BaseType.FullName, type.FullName, cspaceType.BaseType.FullName);
                    SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType); 
                    return false;
                }
            }
 
            // Load the properties for this type
            if (!TryCreateMembers(type, (StructuralType)cspaceType, ospaceType, referenceResolutionListForCurrentType)) 
            { 
                return false;
            } 

            // Add this to the known type map so we won't try to load it again
            SessionData.TypesInLoading.Add(type.FullName, ospaceType);
 
            // we only add the referenceResolution to the list unless we structrually matched this type
            foreach (var referenceResolution in referenceResolutionListForCurrentType) 
            { 
                this._referenceResolutions.Add(referenceResolution);
            } 

            newOSpaceType = ospaceType;
            return true;
        } 

        internal override void OnLevel1SessionProcessing() 
        { 
            CreateRelationships();
 
            foreach (Action resolve in _referenceResolutions)
            {
                resolve();
            } 

            base.OnLevel1SessionProcessing(); 
        } 

        private EdmType ResolveBaseType(StructuralType baseCSpaceType, Type type) 
        {
            StructuralType ospaceType;
            bool foundValue = SessionData.CspaceToOspace.TryGetValue(baseCSpaceType, out ospaceType);
            if (!foundValue) 
            {
                string message = 
                    SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs( 
                        Strings.Validator_OSpace_Convention_BaseTypeNotLoaded(type, baseCSpaceType),
                        baseCSpaceType); 
                SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType));
            }
            return ospaceType;
        } 

        private bool TryCreateMembers(Type type, StructuralType cspaceType, StructuralType ospaceType, List referenceResolutionListForCurrentType) 
        { 
            BindingFlags flags = cspaceType.BaseType == null ? RootEntityPropertyReflectionBindingFlags : PropertyReflectionBindingFlags;
 
            PropertyInfo[] clrProperties = type.GetProperties(flags);

            // required properties scalar properties first
            if (!TryFindAndCreateScalarProperties(type, cspaceType, ospaceType, clrProperties)) 
            {
                return false; 
            } 

            if (!TryFindComplexProperties(type, cspaceType, ospaceType, clrProperties, referenceResolutionListForCurrentType)) 
            {
                return false;
            }
 
            if (!TryFindNavigationProperties(type, cspaceType, ospaceType, clrProperties, referenceResolutionListForCurrentType))
            { 
                return false; 
            }
 
            return true;
        }

        private bool TryFindComplexProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties, List referenceResolutionListForCurrentType) 
        {
            List> typeClosureToTrack = 
                new List>(); 
            foreach(EdmProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers().Where(m => Helper.IsComplexType(m.TypeUsage.EdmType)))
            { 
                PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => MemberMatchesByConvention(p, cspaceProperty));
                if (clrProperty != null)
                {
                    typeClosureToTrack.Add( 
                        new KeyValuePair(
                            cspaceProperty, clrProperty)); 
                } 
                else
                { 
                    string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(cspaceProperty.Name, type.FullName);
                    SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                    return false;
                } 
            }
 
            foreach (var typeToTrack in typeClosureToTrack) 
            {
                TrackClosure(typeToTrack.Value.PropertyType); 
                // prevent the lifting of these closure variables
                var ot = ospaceType;
                var cp = typeToTrack.Key;
                var clrp = typeToTrack.Value; 
                referenceResolutionListForCurrentType.Add(() => CreateAndAddComplexType(type, ot, cp, clrp));
            } 
 
            return true;
        } 

        private bool TryFindNavigationProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties, List referenceResolutionListForCurrentType)
        {
            List> typeClosureToTrack = 
                new List>();
            foreach (NavigationProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers()) 
            { 
                PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => NonPrimitiveMemberMatchesByConvention(p, cspaceProperty));
                if (clrProperty != null) 
                {
                    bool needsSetter = cspaceProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many;
                    if (clrProperty.CanRead && (!needsSetter || clrProperty.CanWrite))
                    { 
                        typeClosureToTrack.Add(
                            new KeyValuePair( 
                                cspaceProperty, clrProperty)); 
                    }
                } 
                else
                {
                    string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(
                        cspaceProperty.Name, type.FullName); 
                    SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                    return false; 
                } 
            }
 
            foreach (var typeToTrack in typeClosureToTrack)
            {
                TrackClosure(typeToTrack.Value.PropertyType);
 
                // keep from lifting these closure variables
                var ct = cspaceType; 
                var ot = ospaceType; 
                var cp = typeToTrack.Key;
                var clrp = typeToTrack.Value; 

                referenceResolutionListForCurrentType.Add(() => CreateAndAddNavigationProperty(ct, ot, cp, clrp));
            }
 
            return true;
        } 
 

        private void TrackClosure(Type type) 
        {

            if (SourceAssembly != type.Assembly &&
                !CacheEntry.ClosureAssemblies.Contains(type.Assembly) && 
                !(type.IsGenericType &&
                  ( 
                    EntityUtil.IsAnICollection(type) || // EntityCollection<>, List<>, ICollection<> 
                    type.GetGenericTypeDefinition() == typeof(System.Data.Objects.DataClasses.EntityReference<>) ||
                    type.GetGenericTypeDefinition() == typeof(System.Nullable<>) 
                  )
                 )
                )
            { 
                CacheEntry.ClosureAssemblies.Add(type.Assembly);
            } 
 
            if (type.IsGenericType)
            { 
                foreach (Type genericArgument in type.GetGenericArguments())
                {
                    TrackClosure(genericArgument);
                } 
            }
        } 
 
        private void CreateAndAddComplexType(Type type, StructuralType ospaceType, EdmProperty cspaceProperty, PropertyInfo clrProperty)
        { 
            StructuralType propertyType;
            if (SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out propertyType))
            {
                EdmProperty property = new EdmProperty(cspaceProperty.Name, TypeUsage.Create(propertyType, new FacetValues { Nullable = false }), clrProperty, type.TypeHandle); 
                ospaceType.AddMember(property);
            } 
            else 
            {
                string message = 
                    SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs(
                        Strings.Validator_OSpace_Convention_MissingOSpaceType(cspaceProperty.TypeUsage.EdmType.FullName),
                        cspaceProperty.TypeUsage.EdmType);
                SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType)); 
            }
 
        } 

        private void CreateAndAddNavigationProperty(StructuralType cspaceType, StructuralType ospaceType, NavigationProperty cspaceProperty, PropertyInfo clrProperty) 
        {
            StructuralType ospaceRelationship;
            if (SessionData.CspaceToOspace.TryGetValue(cspaceProperty.RelationshipType, out ospaceRelationship))
            { 

                bool foundTarget = false; 
                EdmType targetType = null; 
                if (Helper.IsCollectionType(cspaceProperty.TypeUsage.EdmType))
                { 
                    StructuralType findType;
                    foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)((CollectionType)cspaceProperty.TypeUsage.EdmType).TypeUsage.EdmType, out findType);
                    if (foundTarget)
                    { 
                        targetType = findType.GetCollectionType();
                    } 
                } 
                else
                { 
                    StructuralType findType;
                    foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out findType);
                    if (foundTarget)
                    { 
                        targetType = findType;
                    } 
                } 

 
                Debug.Assert(foundTarget, "Since the relationship will only be created if it can find the types for both ends, we will never fail to find one of the ends");

                NavigationProperty navigationProperty = new NavigationProperty(cspaceProperty.Name, TypeUsage.Create(targetType), clrProperty);
                navigationProperty.RelationshipType = (RelationshipType)ospaceRelationship; 

                // we can use First because o-space relationships are created directly from 
                // c-space relationship 
                navigationProperty.ToEndMember = (RelationshipEndMember)((RelationshipType)ospaceRelationship).Members.First(e => e.Name == cspaceProperty.ToEndMember.Name);
                navigationProperty.FromEndMember = (RelationshipEndMember)((RelationshipType)ospaceRelationship).Members.First(e => e.Name == cspaceProperty.FromEndMember.Name); 
                ospaceType.AddMember(navigationProperty);
            }
            else
            { 
                EntityTypeBase missingType = cspaceProperty.RelationshipType.RelationshipEndMembers.Select(e => ((RefType)e.TypeUsage.EdmType).ElementType).First(e => e != cspaceType);
                string message = 
                    SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs( 
                        Strings.Validator_OSpace_Convention_RelationshipNotLoaded(cspaceProperty.RelationshipType.FullName, missingType.FullName),
                        missingType); 
                SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType));
            }
        }
 
        private bool TryFindAndCreateScalarProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties)
        { 
            foreach (EdmProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers().Where(p => Helper.IsPrimitiveType(p.TypeUsage.EdmType))) 
            {
                PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => MemberMatchesByConvention(p, cspaceProperty)); 
                if (clrProperty != null)
                {
                    PrimitiveType propertyType;
                    bool typeIsNullable; 
                    if (TryGetPrimitiveType(clrProperty.PropertyType, out typeIsNullable, out propertyType))
                    { 
                        if (clrProperty.CanRead && clrProperty.CanWrite) 
                        {
                            bool isKeyMember = Helper.IsEntityType(cspaceType) && ((EntityType)cspaceType).KeyMemberNames.Contains(clrProperty.Name); 
                            EdmProperty ospaceProperty = new EdmProperty(cspaceProperty.Name, TypeUsage.Create(propertyType, new FacetValues { Nullable = typeIsNullable && !isKeyMember }), clrProperty, type.TypeHandle);
                            if (isKeyMember)
                            {
                                ((EntityType)ospaceType).AddKeyMember(ospaceProperty); 
                            }
                            else 
                            { 
                                ospaceType.AddMember(ospaceProperty);
                            } 
                        }
                        else
                        {
                            string message = Strings.Validator_OSpace_Convention_ScalarPropertyMissginGetterOrSetter(clrProperty.Name, type.FullName, type.Assembly.FullName); 
                            SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                            return false; 
                        } 
                    }
                    else 
                    {
                        string message = Strings.Validator_OSpace_Convention_NonPrimitiveTypeProperty(clrProperty.Name, type.FullName, clrProperty.PropertyType.FullName);
                        SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                        return false; 
                    }
                } 
                else 
                {
                    string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(cspaceProperty.Name, type.FullName); 
                    SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                    return false;
                }
            } 
            return true;
        } 
 
        private void CreateRelationships()
        { 
            if (SessionData.ConventionBasedRelationshipsAreLoaded)
            {
                return;
            } 

 
            SessionData.ConventionBasedRelationshipsAreLoaded = true; 

            // find all the relationships 
            foreach (AssociationType cspaceAssociation in SessionData.EdmItemCollection.GetItems())
            {
                Debug.Assert(cspaceAssociation.RelationshipEndMembers.Count == 2, "Relationships are assumed to have exactly two ends");
 
                if (SessionData.CspaceToOspace.ContainsKey(cspaceAssociation))
                { 
                    // don't try to load relationships that we already know about 
                    continue;
                } 

                StructuralType [] ospaceEndTypes = new StructuralType [2];
                if (SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[0]), out ospaceEndTypes[0]) &&
                    SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[1]), out ospaceEndTypes[1])) 
                {
                    // if we can find both ends of the relationship, then create it 
 
                    AssociationType ospaceAssociation = new AssociationType(cspaceAssociation.Name, cspaceAssociation.NamespaceName, cspaceAssociation.IsForeignKey, DataSpace.OSpace);
                    for (int i = 0; i < cspaceAssociation.RelationshipEndMembers.Count; i++) 
                    {
                        EntityType ospaceEndType = (EntityType)ospaceEndTypes[i];
                        RelationshipEndMember cspaceEnd = cspaceAssociation.RelationshipEndMembers[i];
 
                        ospaceAssociation.AddKeyMember(new AssociationEndMember(cspaceEnd.Name, ospaceEndType.GetReferenceType(), cspaceEnd.RelationshipMultiplicity));
                    } 
                    CacheEntry.TypesInAssembly.Add(ospaceAssociation); 
                    SessionData.TypesInLoading.Add(ospaceAssociation.FullName, ospaceAssociation);
                    SessionData.CspaceToOspace.Add(cspaceAssociation, ospaceAssociation); 

                }
            }
        } 

        private StructuralType GetRelationshipEndType(RelationshipEndMember relationshipEndMember) 
        { 
            return ((RefType)relationshipEndMember.TypeUsage.EdmType).ElementType;
        } 


        private bool MemberMatchesByConvention(PropertyInfo clrProperty, EdmMember cspaceMember)
        { 
            return clrProperty.Name == cspaceMember.Name;
        } 
 
        private bool NonPrimitiveMemberMatchesByConvention(PropertyInfo clrProperty, EdmMember cspaceMember)
        { 
            return !clrProperty.PropertyType.IsValueType && !clrProperty.PropertyType.IsAssignableFrom(typeof(string)) && clrProperty.Name == cspaceMember.Name;
        }

        internal static bool SessionContainsConventionParameters(ObjectItemLoadingSessionData sessionData) 
        {
            return sessionData.EdmItemCollection != null; 
        } 

 
        internal static bool TypesMatchByConvention(Type type, EdmType cspaceType)
        {
            return type.Name == cspaceType.Name;
        } 

        internal static ObjectItemAssemblyLoader Create(Assembly assembly, ObjectItemLoadingSessionData sessionData) 
        { 
            if (!ObjectItemAttributeAssemblyLoader.IsSchemaAttributePresent(assembly))
            { 
                return new ObjectItemConventionAssemblyLoader(assembly, sessionData);
            }
            else
            { 
                // we were loading in convention mode, and ran into an assembly that can't be loaded by convention
                sessionData.EdmItemErrors.Add(new EdmItemError(Strings.Validator_OSpace_Convention_AttributeAssemblyReferenced(assembly.FullName), null)); 
                return new ObjectItemNoOpAssemblyLoader(assembly, sessionData); 
            }
        } 
    }
}

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

 
using System.Reflection;
using System.Linq;
using System.Diagnostics;
using System.Collections.Generic; 
using System.Data.Entity;
using System.Text; 
 
namespace System.Data.Metadata.Edm
{ 
    internal sealed class ObjectItemConventionAssemblyLoader : ObjectItemAssemblyLoader
    {
        // for root entities, entities with no base type, we will additionally look
        // at properties on the clr base hierarchy. 
        private const BindingFlags RootEntityPropertyReflectionBindingFlags = PropertyReflectionBindingFlags & ~BindingFlags.DeclaredOnly | BindingFlags.FlattenHierarchy;
 
        private new MutableAssemblyCacheEntry CacheEntry { get { return (MutableAssemblyCacheEntry)base.CacheEntry; } } 
        private List _referenceResolutions = new List();
 
        internal ObjectItemConventionAssemblyLoader(Assembly assembly, ObjectItemLoadingSessionData sessionData)
            : base(assembly, new MutableAssemblyCacheEntry(), sessionData)
        {
            Debug.Assert(Create == sessionData.ObjectItemAssemblyLoaderFactory, "Why is there a different factory creating this class"); 
            SessionData.RegisterForLevel1PostSessionProcessing(this);
        } 
 
        protected override void LoadTypesFromAssembly()
        { 
            foreach (Type type in SourceAssembly.GetTypes())
            {
                StructuralType cspaceType;
                if (TryGetCSpaceTypeMatch(type, out cspaceType)) 
                {
                    if (type.IsValueType) 
                    { 
                        SessionData.LoadMessageLogger.LogLoadMessage(Strings.Validator_OSpace_Convention_Struct(cspaceType.FullName, type.FullName), cspaceType);
                        continue; 
                    }

                    EdmType ospaceType;
                    if (TryCreateType(type, (StructuralType)cspaceType, out ospaceType)) 
                    {
                        CacheEntry.TypesInAssembly.Add(ospaceType); 
                        // check for duplcates so we don't cause an ArgumentException, 
                        // Mapping will do the actual error for the duplicate type later
                        if (!SessionData.CspaceToOspace.ContainsKey((StructuralType)cspaceType)) 
                        {
                            SessionData.CspaceToOspace.Add((StructuralType)cspaceType, (StructuralType)ospaceType);
                        }
                        else 
                        {
                            // at this point there is already a Clr Type that is structurally matched to this CSpace type, we throw exception 
                            StructuralType previousOSpaceType = SessionData.CspaceToOspace[cspaceType]; 
                            SessionData.EdmItemErrors.Add(
                                new EdmItemError(Strings.Validator_OSpace_Convention_AmbiguousClrType(cspaceType.Name, previousOSpaceType.ClrType.FullName, type.FullName), previousOSpaceType)); 
                        }
                    }
                }
            } 

            if (SessionData.TypesInLoading.Count == 0) 
            { 
                Debug.Assert(CacheEntry.ClosureAssemblies.Count == 0, "How did we get closure assemblies?");
 
                // since we didn't find any types, don't lock into convention based
                SessionData.ObjectItemAssemblyLoaderFactory = null;
            }
        } 

 
        protected override void AddToAssembliesLoaded() 
        {
            SessionData.AssembliesLoaded.Add(SourceAssembly, CacheEntry); 
        }

        private bool TryGetCSpaceTypeMatch(Type type, out StructuralType cspaceType)
        { 
            // brute force try and find a matching name
            KeyValuePair pair; 
            if (SessionData.ConventionCSpaceTypeNames.TryGetValue(type.Name, out pair)) 
            {
                if (pair.Value == 1) 
                {
                    // we found a type match
                    cspaceType = pair.Key;
                    return true; 
                }
                else 
                { 
                    Debug.Assert(pair.Value > 1, "how did we get a negative count of types in the dictionary?");
                    SessionData.EdmItemErrors.Add(new EdmItemError(Strings.Validator_OSpace_Convention_MultipleTypesWithSameName(type.Name), pair.Key)); 
                }
            }

            cspaceType = null; 
            return false;
        } 
 
        private bool TryCreateType(Type type, StructuralType cspaceType, out EdmType newOSpaceType)
        { 
            List referenceResolutionListForCurrentType = new List();
            newOSpaceType = null;
            Debug.Assert(TypesMatchByConvention(type, cspaceType), "The types passed as parameters don't match by convention.");
 
            StructuralType ospaceType;
            if (Helper.IsEntityType(cspaceType)) 
            { 
                ospaceType = new ClrEntityType(type, cspaceType.NamespaceName, cspaceType.Name);
            } 
            else
            {
                Debug.Assert(Helper.IsComplexType(cspaceType), "Invalid type attribute encountered");
                ospaceType = new ClrComplexType(type, cspaceType.NamespaceName, cspaceType.Name); 
            }
 
            if (cspaceType.BaseType != null) 
            {
                if (TypesMatchByConvention(type.BaseType, cspaceType.BaseType)) 
                {
                    TrackClosure(type.BaseType);
                    referenceResolutionListForCurrentType.Add(
                        () => ospaceType.BaseType = ResolveBaseType((StructuralType)cspaceType.BaseType, type)); 
                }
                else 
                { 
                    string message = Strings.Validator_OSpace_Convention_BaseTypeIncompatible(type.BaseType.FullName, type.FullName, cspaceType.BaseType.FullName);
                    SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType); 
                    return false;
                }
            }
 
            // Load the properties for this type
            if (!TryCreateMembers(type, (StructuralType)cspaceType, ospaceType, referenceResolutionListForCurrentType)) 
            { 
                return false;
            } 

            // Add this to the known type map so we won't try to load it again
            SessionData.TypesInLoading.Add(type.FullName, ospaceType);
 
            // we only add the referenceResolution to the list unless we structrually matched this type
            foreach (var referenceResolution in referenceResolutionListForCurrentType) 
            { 
                this._referenceResolutions.Add(referenceResolution);
            } 

            newOSpaceType = ospaceType;
            return true;
        } 

        internal override void OnLevel1SessionProcessing() 
        { 
            CreateRelationships();
 
            foreach (Action resolve in _referenceResolutions)
            {
                resolve();
            } 

            base.OnLevel1SessionProcessing(); 
        } 

        private EdmType ResolveBaseType(StructuralType baseCSpaceType, Type type) 
        {
            StructuralType ospaceType;
            bool foundValue = SessionData.CspaceToOspace.TryGetValue(baseCSpaceType, out ospaceType);
            if (!foundValue) 
            {
                string message = 
                    SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs( 
                        Strings.Validator_OSpace_Convention_BaseTypeNotLoaded(type, baseCSpaceType),
                        baseCSpaceType); 
                SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType));
            }
            return ospaceType;
        } 

        private bool TryCreateMembers(Type type, StructuralType cspaceType, StructuralType ospaceType, List referenceResolutionListForCurrentType) 
        { 
            BindingFlags flags = cspaceType.BaseType == null ? RootEntityPropertyReflectionBindingFlags : PropertyReflectionBindingFlags;
 
            PropertyInfo[] clrProperties = type.GetProperties(flags);

            // required properties scalar properties first
            if (!TryFindAndCreateScalarProperties(type, cspaceType, ospaceType, clrProperties)) 
            {
                return false; 
            } 

            if (!TryFindComplexProperties(type, cspaceType, ospaceType, clrProperties, referenceResolutionListForCurrentType)) 
            {
                return false;
            }
 
            if (!TryFindNavigationProperties(type, cspaceType, ospaceType, clrProperties, referenceResolutionListForCurrentType))
            { 
                return false; 
            }
 
            return true;
        }

        private bool TryFindComplexProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties, List referenceResolutionListForCurrentType) 
        {
            List> typeClosureToTrack = 
                new List>(); 
            foreach(EdmProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers().Where(m => Helper.IsComplexType(m.TypeUsage.EdmType)))
            { 
                PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => MemberMatchesByConvention(p, cspaceProperty));
                if (clrProperty != null)
                {
                    typeClosureToTrack.Add( 
                        new KeyValuePair(
                            cspaceProperty, clrProperty)); 
                } 
                else
                { 
                    string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(cspaceProperty.Name, type.FullName);
                    SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                    return false;
                } 
            }
 
            foreach (var typeToTrack in typeClosureToTrack) 
            {
                TrackClosure(typeToTrack.Value.PropertyType); 
                // prevent the lifting of these closure variables
                var ot = ospaceType;
                var cp = typeToTrack.Key;
                var clrp = typeToTrack.Value; 
                referenceResolutionListForCurrentType.Add(() => CreateAndAddComplexType(type, ot, cp, clrp));
            } 
 
            return true;
        } 

        private bool TryFindNavigationProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties, List referenceResolutionListForCurrentType)
        {
            List> typeClosureToTrack = 
                new List>();
            foreach (NavigationProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers()) 
            { 
                PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => NonPrimitiveMemberMatchesByConvention(p, cspaceProperty));
                if (clrProperty != null) 
                {
                    bool needsSetter = cspaceProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many;
                    if (clrProperty.CanRead && (!needsSetter || clrProperty.CanWrite))
                    { 
                        typeClosureToTrack.Add(
                            new KeyValuePair( 
                                cspaceProperty, clrProperty)); 
                    }
                } 
                else
                {
                    string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(
                        cspaceProperty.Name, type.FullName); 
                    SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                    return false; 
                } 
            }
 
            foreach (var typeToTrack in typeClosureToTrack)
            {
                TrackClosure(typeToTrack.Value.PropertyType);
 
                // keep from lifting these closure variables
                var ct = cspaceType; 
                var ot = ospaceType; 
                var cp = typeToTrack.Key;
                var clrp = typeToTrack.Value; 

                referenceResolutionListForCurrentType.Add(() => CreateAndAddNavigationProperty(ct, ot, cp, clrp));
            }
 
            return true;
        } 
 

        private void TrackClosure(Type type) 
        {

            if (SourceAssembly != type.Assembly &&
                !CacheEntry.ClosureAssemblies.Contains(type.Assembly) && 
                !(type.IsGenericType &&
                  ( 
                    EntityUtil.IsAnICollection(type) || // EntityCollection<>, List<>, ICollection<> 
                    type.GetGenericTypeDefinition() == typeof(System.Data.Objects.DataClasses.EntityReference<>) ||
                    type.GetGenericTypeDefinition() == typeof(System.Nullable<>) 
                  )
                 )
                )
            { 
                CacheEntry.ClosureAssemblies.Add(type.Assembly);
            } 
 
            if (type.IsGenericType)
            { 
                foreach (Type genericArgument in type.GetGenericArguments())
                {
                    TrackClosure(genericArgument);
                } 
            }
        } 
 
        private void CreateAndAddComplexType(Type type, StructuralType ospaceType, EdmProperty cspaceProperty, PropertyInfo clrProperty)
        { 
            StructuralType propertyType;
            if (SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out propertyType))
            {
                EdmProperty property = new EdmProperty(cspaceProperty.Name, TypeUsage.Create(propertyType, new FacetValues { Nullable = false }), clrProperty, type.TypeHandle); 
                ospaceType.AddMember(property);
            } 
            else 
            {
                string message = 
                    SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs(
                        Strings.Validator_OSpace_Convention_MissingOSpaceType(cspaceProperty.TypeUsage.EdmType.FullName),
                        cspaceProperty.TypeUsage.EdmType);
                SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType)); 
            }
 
        } 

        private void CreateAndAddNavigationProperty(StructuralType cspaceType, StructuralType ospaceType, NavigationProperty cspaceProperty, PropertyInfo clrProperty) 
        {
            StructuralType ospaceRelationship;
            if (SessionData.CspaceToOspace.TryGetValue(cspaceProperty.RelationshipType, out ospaceRelationship))
            { 

                bool foundTarget = false; 
                EdmType targetType = null; 
                if (Helper.IsCollectionType(cspaceProperty.TypeUsage.EdmType))
                { 
                    StructuralType findType;
                    foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)((CollectionType)cspaceProperty.TypeUsage.EdmType).TypeUsage.EdmType, out findType);
                    if (foundTarget)
                    { 
                        targetType = findType.GetCollectionType();
                    } 
                } 
                else
                { 
                    StructuralType findType;
                    foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out findType);
                    if (foundTarget)
                    { 
                        targetType = findType;
                    } 
                } 

 
                Debug.Assert(foundTarget, "Since the relationship will only be created if it can find the types for both ends, we will never fail to find one of the ends");

                NavigationProperty navigationProperty = new NavigationProperty(cspaceProperty.Name, TypeUsage.Create(targetType), clrProperty);
                navigationProperty.RelationshipType = (RelationshipType)ospaceRelationship; 

                // we can use First because o-space relationships are created directly from 
                // c-space relationship 
                navigationProperty.ToEndMember = (RelationshipEndMember)((RelationshipType)ospaceRelationship).Members.First(e => e.Name == cspaceProperty.ToEndMember.Name);
                navigationProperty.FromEndMember = (RelationshipEndMember)((RelationshipType)ospaceRelationship).Members.First(e => e.Name == cspaceProperty.FromEndMember.Name); 
                ospaceType.AddMember(navigationProperty);
            }
            else
            { 
                EntityTypeBase missingType = cspaceProperty.RelationshipType.RelationshipEndMembers.Select(e => ((RefType)e.TypeUsage.EdmType).ElementType).First(e => e != cspaceType);
                string message = 
                    SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs( 
                        Strings.Validator_OSpace_Convention_RelationshipNotLoaded(cspaceProperty.RelationshipType.FullName, missingType.FullName),
                        missingType); 
                SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType));
            }
        }
 
        private bool TryFindAndCreateScalarProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties)
        { 
            foreach (EdmProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers().Where(p => Helper.IsPrimitiveType(p.TypeUsage.EdmType))) 
            {
                PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => MemberMatchesByConvention(p, cspaceProperty)); 
                if (clrProperty != null)
                {
                    PrimitiveType propertyType;
                    bool typeIsNullable; 
                    if (TryGetPrimitiveType(clrProperty.PropertyType, out typeIsNullable, out propertyType))
                    { 
                        if (clrProperty.CanRead && clrProperty.CanWrite) 
                        {
                            bool isKeyMember = Helper.IsEntityType(cspaceType) && ((EntityType)cspaceType).KeyMemberNames.Contains(clrProperty.Name); 
                            EdmProperty ospaceProperty = new EdmProperty(cspaceProperty.Name, TypeUsage.Create(propertyType, new FacetValues { Nullable = typeIsNullable && !isKeyMember }), clrProperty, type.TypeHandle);
                            if (isKeyMember)
                            {
                                ((EntityType)ospaceType).AddKeyMember(ospaceProperty); 
                            }
                            else 
                            { 
                                ospaceType.AddMember(ospaceProperty);
                            } 
                        }
                        else
                        {
                            string message = Strings.Validator_OSpace_Convention_ScalarPropertyMissginGetterOrSetter(clrProperty.Name, type.FullName, type.Assembly.FullName); 
                            SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                            return false; 
                        } 
                    }
                    else 
                    {
                        string message = Strings.Validator_OSpace_Convention_NonPrimitiveTypeProperty(clrProperty.Name, type.FullName, clrProperty.PropertyType.FullName);
                        SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                        return false; 
                    }
                } 
                else 
                {
                    string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(cspaceProperty.Name, type.FullName); 
                    SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
                    return false;
                }
            } 
            return true;
        } 
 
        private void CreateRelationships()
        { 
            if (SessionData.ConventionBasedRelationshipsAreLoaded)
            {
                return;
            } 

 
            SessionData.ConventionBasedRelationshipsAreLoaded = true; 

            // find all the relationships 
            foreach (AssociationType cspaceAssociation in SessionData.EdmItemCollection.GetItems())
            {
                Debug.Assert(cspaceAssociation.RelationshipEndMembers.Count == 2, "Relationships are assumed to have exactly two ends");
 
                if (SessionData.CspaceToOspace.ContainsKey(cspaceAssociation))
                { 
                    // don't try to load relationships that we already know about 
                    continue;
                } 

                StructuralType [] ospaceEndTypes = new StructuralType [2];
                if (SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[0]), out ospaceEndTypes[0]) &&
                    SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[1]), out ospaceEndTypes[1])) 
                {
                    // if we can find both ends of the relationship, then create it 
 
                    AssociationType ospaceAssociation = new AssociationType(cspaceAssociation.Name, cspaceAssociation.NamespaceName, cspaceAssociation.IsForeignKey, DataSpace.OSpace);
                    for (int i = 0; i < cspaceAssociation.RelationshipEndMembers.Count; i++) 
                    {
                        EntityType ospaceEndType = (EntityType)ospaceEndTypes[i];
                        RelationshipEndMember cspaceEnd = cspaceAssociation.RelationshipEndMembers[i];
 
                        ospaceAssociation.AddKeyMember(new AssociationEndMember(cspaceEnd.Name, ospaceEndType.GetReferenceType(), cspaceEnd.RelationshipMultiplicity));
                    } 
                    CacheEntry.TypesInAssembly.Add(ospaceAssociation); 
                    SessionData.TypesInLoading.Add(ospaceAssociation.FullName, ospaceAssociation);
                    SessionData.CspaceToOspace.Add(cspaceAssociation, ospaceAssociation); 

                }
            }
        } 

        private StructuralType GetRelationshipEndType(RelationshipEndMember relationshipEndMember) 
        { 
            return ((RefType)relationshipEndMember.TypeUsage.EdmType).ElementType;
        } 


        private bool MemberMatchesByConvention(PropertyInfo clrProperty, EdmMember cspaceMember)
        { 
            return clrProperty.Name == cspaceMember.Name;
        } 
 
        private bool NonPrimitiveMemberMatchesByConvention(PropertyInfo clrProperty, EdmMember cspaceMember)
        { 
            return !clrProperty.PropertyType.IsValueType && !clrProperty.PropertyType.IsAssignableFrom(typeof(string)) && clrProperty.Name == cspaceMember.Name;
        }

        internal static bool SessionContainsConventionParameters(ObjectItemLoadingSessionData sessionData) 
        {
            return sessionData.EdmItemCollection != null; 
        } 

 
        internal static bool TypesMatchByConvention(Type type, EdmType cspaceType)
        {
            return type.Name == cspaceType.Name;
        } 

        internal static ObjectItemAssemblyLoader Create(Assembly assembly, ObjectItemLoadingSessionData sessionData) 
        { 
            if (!ObjectItemAttributeAssemblyLoader.IsSchemaAttributePresent(assembly))
            { 
                return new ObjectItemConventionAssemblyLoader(assembly, sessionData);
            }
            else
            { 
                // we were loading in convention mode, and ran into an assembly that can't be loaded by convention
                sessionData.EdmItemErrors.Add(new EdmItemError(Strings.Validator_OSpace_Convention_AttributeAssemblyReferenced(assembly.FullName), null)); 
                return new ObjectItemNoOpAssemblyLoader(assembly, sessionData); 
            }
        } 
    }
}

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

                        

Link Menu

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