//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- namespace System.Data.Metadata.Edm { using System.Collections.Generic; using System.Data.Entity; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; 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 EntityUtil.GetTypesSpecial(SourceAssembly)) { EdmType cspaceType; if (TryGetCSpaceTypeMatch(type, out cspaceType)) { if (type.IsValueType && !type.IsEnum) { SessionData.LoadMessageLogger.LogLoadMessage(Strings.Validator_OSpace_Convention_Struct(cspaceType.FullName, type.FullName), cspaceType); continue; } EdmType ospaceType; if (TryCreateType(type, cspaceType, out ospaceType)) { Debug.Assert(ospaceType is StructuralType || Helper.IsEnumType(ospaceType), "Only StructuralType or EnumType expected."); CacheEntry.TypesInAssembly.Add(ospaceType); // check for duplicates so we don't cause an ArgumentException, // Mapping will do the actual error for the duplicate type later if (!SessionData.CspaceToOspace.ContainsKey(cspaceType)) { SessionData.CspaceToOspace.Add(cspaceType, ospaceType); } else { // at this point there is already a Clr Type that is structurally matched to this CSpace type, we throw exception EdmType 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 EdmType 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; } /// /// Creates a structural or enum OSpace type based on CLR type and CSpace type. /// /// CLR type. /// CSpace Type /// OSpace type created based on CLR and /// true if the type was created successfully. Otherwise false. private bool TryCreateType(Type type, EdmType cspaceType, out EdmType newOSpaceType) { Debug.Assert(type != null, "type != null"); Debug.Assert(cspaceType != null, "cspaceType != null"); Debug.Assert(cspaceType is StructuralType || Helper.IsEnumType(cspaceType), "Structural or enum type expected"); newOSpaceType = null; // if one of the types is an enum while the other is not there is no match if (Helper.IsEnumType(cspaceType) ^ type.IsEnum) { SessionData.LoadMessageLogger.LogLoadMessage( Strings.Validator_OSpace_Convention_SSpaceOSpaceTypeMismatch(cspaceType.FullName, cspaceType.FullName), cspaceType); return false; } if(Helper.IsEnumType(cspaceType)) { return TryCreateEnumType(type, (EnumType)cspaceType, out newOSpaceType); } else { Debug.Assert(cspaceType is StructuralType); return TryCreateStructuralType(type, (StructuralType)cspaceType, out newOSpaceType); } } /// /// Creates a structural OSpace type based on CLR type and CSpace type. /// /// CLR type. /// CSpace Type /// OSpace type created based on CLR and /// true if the type was created successfully. Otherwise false. private bool TryCreateStructuralType(Type type, StructuralType cspaceType, out EdmType newOSpaceType) { Debug.Assert(type != null, "type != null"); Debug.Assert(cspaceType != null, "cspaceType != null"); 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; } /// /// Creates new enum OSpace type built based on CLR and /// /// CLR type to create OSpace type from. /// CSpace type used to get namespace and name for the newly created OSpace type. /// /// New enum OSpace type built based on CLR and or null /// if the type could not be built. /// /// true if the type was built successfully.false otherwise. private bool TryCreateEnumType(Type enumType, EnumType cspaceEnumType, out EdmType newOSpaceType) { Debug.Assert(enumType != null, "enumType != null"); Debug.Assert(enumType.IsEnum, "enum type expected"); Debug.Assert(cspaceEnumType != null, "cspaceEnumType != null"); Debug.Assert(Helper.IsEnumType(cspaceEnumType), "Enum type expected"); Debug.Assert(TypesMatchByConvention(enumType, cspaceEnumType), "The types passed as parameters don't match by convention."); newOSpaceType = null; // Check if the OSpace and CSpace enum type match if (!UnderlyingEnumTypesMatch(enumType, cspaceEnumType) || !EnumMembersMatch(enumType, cspaceEnumType)) { return false; } newOSpaceType = new ClrEnumType(enumType, cspaceEnumType.NamespaceName, cspaceEnumType.Name); SessionData.TypesInLoading.Add(enumType.FullName, newOSpaceType); return true; } /// /// Verifies whether underlying types of CLR and EDM types match /// /// OSpace CLR enum type. /// CSpace EDM enum type. /// true if types match. false otherwise. private bool UnderlyingEnumTypesMatch(Type enumType, EnumType cspaceEnumType) { Debug.Assert(enumType != null, "enumType != null"); Debug.Assert(enumType.IsEnum, "expected enum OSpace type"); Debug.Assert(cspaceEnumType != null, "cspaceEnumType != null"); Debug.Assert(Helper.IsEnumType(cspaceEnumType), "Enum type expected"); // Note that TryGetPrimitiveType() will return false not only for types that are not primitive // but also for CLR primitive types that are valid underlying enum types in CLR but are not // a valid Edm primitive types (e.g. ulong) PrimitiveType underlyingEnumType; if (!ClrProviderManifest.Instance.TryGetPrimitiveType(enumType.GetEnumUnderlyingType(), out underlyingEnumType)) { SessionData.LoadMessageLogger.LogLoadMessage( Strings.Validator_UnsupportedEnumUnderlyingType(enumType.GetEnumUnderlyingType().FullName), cspaceEnumType); return false; } else if (underlyingEnumType.PrimitiveTypeKind != cspaceEnumType.UnderlyingType.PrimitiveTypeKind) { SessionData.LoadMessageLogger.LogLoadMessage( Strings.Validator_OSpace_Convention_NonMatchingUnderlyingTypes, cspaceEnumType); return false; } return true; } /// /// Verifies whether enum members of CLR and EDM types match. /// /// OSpace CLR enum type. /// CSpace EDM enum type. /// true if members match. false otherwise. private bool EnumMembersMatch(Type enumType, EnumType cspaceEnumType) { Debug.Assert(enumType != null, "enumType != null"); Debug.Assert(enumType.IsEnum, "expected enum OSpace type"); Debug.Assert(cspaceEnumType != null, "cspaceEnumType != null"); Debug.Assert(Helper.IsEnumType(cspaceEnumType), "Enum type expected"); Debug.Assert(cspaceEnumType.UnderlyingType.ClrEquivalentType == enumType.GetEnumUnderlyingType(), "underlying types should have already been checked"); var enumUnderlyingType = enumType.GetEnumUnderlyingType(); var cspaceSortedEnumMemberEnumerator = cspaceEnumType.Members.OrderBy(m => m.Name).GetEnumerator(); var ospaceSortedEnumMemberNamesEnumerator = enumType.GetEnumNames().OrderBy(n => n).GetEnumerator(); // no checks required if edm enum type does not have any members if (!cspaceSortedEnumMemberEnumerator.MoveNext()) { return true; } while (ospaceSortedEnumMemberNamesEnumerator.MoveNext()) { if (cspaceSortedEnumMemberEnumerator.Current.Name == ospaceSortedEnumMemberNamesEnumerator.Current && cspaceSortedEnumMemberEnumerator.Current.Value.Equals( Convert.ChangeType( Enum.Parse(enumType, ospaceSortedEnumMemberNamesEnumerator.Current), enumUnderlyingType, CultureInfo.InvariantCulture))) { if (!cspaceSortedEnumMemberEnumerator.MoveNext()) { return true; } } } SessionData.LoadMessageLogger.LogLoadMessage( System.Data.Entity.Strings.Mapping_Enum_OCMapping_MemberMismatch( enumType.FullName, cspaceSortedEnumMemberEnumerator.Current.Name, cspaceSortedEnumMemberEnumerator.Current.Value, cspaceEnumType.FullName), cspaceEnumType); return false; } internal override void OnLevel1SessionProcessing() { CreateRelationships(); foreach (Action resolve in _referenceResolutions) { resolve(); } base.OnLevel1SessionProcessing(); } private EdmType ResolveBaseType(StructuralType baseCSpaceType, Type type) { EdmType 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)); } Debug.Assert(!foundValue || ospaceType is StructuralType, "Structural type expected (if found)."); 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 (!TryFindAndCreatePrimitiveProperties(type, cspaceType, ospaceType, clrProperties)) { return false; } if(!TryFindAndCreateEnumProperties(type, cspaceType, ospaceType, clrProperties, referenceResolutionListForCurrentType)) { 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) { EdmType propertyType; if (SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out propertyType)) { Debug.Assert(propertyType is StructuralType, "Structural type expected."); 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) { EdmType ospaceRelationship; if (SessionData.CspaceToOspace.TryGetValue(cspaceProperty.RelationshipType, out ospaceRelationship)) { Debug.Assert(ospaceRelationship is StructuralType, "Structural type expected."); bool foundTarget = false; EdmType targetType = null; if (Helper.IsCollectionType(cspaceProperty.TypeUsage.EdmType)) { EdmType findType; foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)((CollectionType)cspaceProperty.TypeUsage.EdmType).TypeUsage.EdmType, out findType); if (foundTarget) { Debug.Assert(findType is StructuralType, "Structural type expected."); targetType = findType.GetCollectionType(); } } else { EdmType findType; foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out findType); if (foundTarget) { Debug.Assert(findType is StructuralType, "Structural type expected."); 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 TryFindAndCreatePrimitiveProperties(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; if (TryGetPrimitiveType(clrProperty.PropertyType, out propertyType)) { if (clrProperty.CanRead && clrProperty.CanWrite) { AddScalarMember(type, clrProperty, ospaceType, cspaceProperty, propertyType); } 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 bool TryFindAndCreateEnumProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties, List referenceResolutionListForCurrentType) { var typeClosureToTrack = new List>(); foreach (EdmProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers().Where(p => Helper.IsEnumType(p.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(() => CreateAndAddEnumProperty(type, ot, cp, clrp)); } return true; } /// /// Creates an Enum property based on and adds it to the parent structural type. /// /// CLR type owning . /// OSpace type the created property will be added to. /// Corresponding property from CSpace. /// CLR property used to build an Enum property. private void CreateAndAddEnumProperty(Type type, StructuralType ospaceType, EdmProperty cspaceProperty, PropertyInfo clrProperty) { EdmType propertyType; if (SessionData.CspaceToOspace.TryGetValue(cspaceProperty.TypeUsage.EdmType, out propertyType)) { if (clrProperty.CanRead && clrProperty.CanWrite) { AddScalarMember(type, clrProperty, ospaceType, cspaceProperty, propertyType); } else { string message = SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs( Strings.Validator_OSpace_Convention_ScalarPropertyMissginGetterOrSetter(clrProperty.Name, type.FullName, type.Assembly.FullName), cspaceProperty.TypeUsage.EdmType); SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType)); } } 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 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; } EdmType[] ospaceEndTypes = new EdmType[2]; if (SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[0]), out ospaceEndTypes[0]) && SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[1]), out ospaceEndTypes[1])) { Debug.Assert(ospaceEndTypes[0] is StructuralType); Debug.Assert(ospaceEndTypes[1] is StructuralType); // 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 static StructuralType GetRelationshipEndType(RelationshipEndMember relationshipEndMember) { return ((RefType)relationshipEndMember.TypeUsage.EdmType).ElementType; } private static bool MemberMatchesByConvention(PropertyInfo clrProperty, EdmMember cspaceMember) { return clrProperty.Name == cspaceMember.Name; } private static 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; } private void AddScalarMember(Type type, PropertyInfo clrProperty, StructuralType ospaceType, EdmProperty cspaceProperty, EdmType propertyType) { Debug.Assert(type != null, "type != null"); Debug.Assert(clrProperty != null, "clrProperty != null"); Debug.Assert(clrProperty.CanRead && clrProperty.CanWrite, "The clr property has to have a setter and a getter."); Debug.Assert(ospaceType != null, "ospaceType != null"); Debug.Assert(cspaceProperty != null, "cspaceProperty != null"); Debug.Assert(propertyType != null, "propertyType != null"); Debug.Assert(Helper.IsScalarType(propertyType), "Property has to be primitive or enum."); var cspaceType = cspaceProperty.DeclaringType; bool isKeyMember = Helper.IsEntityType(cspaceType) && ((EntityType)cspaceType).KeyMemberNames.Contains(clrProperty.Name); // the property is nullable only if it is not a key and can actually be set to null (i.e. is not a value type or is a nullable value type) bool nullableFacetValue = !isKeyMember && (!clrProperty.PropertyType.IsValueType || Nullable.GetUnderlyingType(clrProperty.PropertyType) != null); EdmProperty ospaceProperty = new EdmProperty( cspaceProperty.Name, TypeUsage.Create(propertyType, new FacetValues { Nullable = nullableFacetValue }), clrProperty, type.TypeHandle); if (isKeyMember) { ((EntityType)ospaceType).AddKeyMember(ospaceProperty); } else { ospaceType.AddMember(ospaceProperty); } } 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); } } } }