1 //---------------------------------------------------------------------
2 // <copyright file="vs.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 //---------------------------------------------------------------------
10 namespace System.Data.Metadata.Edm
12 using System.Collections.Generic;
13 using System.Data.Entity;
14 using System.Diagnostics;
15 using System.Globalization;
17 using System.Reflection;
19 internal sealed class ObjectItemConventionAssemblyLoader : ObjectItemAssemblyLoader
21 // for root entities, entities with no base type, we will additionally look
22 // at properties on the clr base hierarchy.
23 private const BindingFlags RootEntityPropertyReflectionBindingFlags = PropertyReflectionBindingFlags & ~BindingFlags.DeclaredOnly | BindingFlags.FlattenHierarchy;
25 private new MutableAssemblyCacheEntry CacheEntry { get { return (MutableAssemblyCacheEntry)base.CacheEntry; } }
26 private List<Action> _referenceResolutions = new List<Action>();
28 internal ObjectItemConventionAssemblyLoader(Assembly assembly, ObjectItemLoadingSessionData sessionData)
29 : base(assembly, new MutableAssemblyCacheEntry(), sessionData)
31 Debug.Assert(Create == sessionData.ObjectItemAssemblyLoaderFactory, "Why is there a different factory creating this class");
32 SessionData.RegisterForLevel1PostSessionProcessing(this);
35 protected override void LoadTypesFromAssembly()
37 foreach (Type type in EntityUtil.GetTypesSpecial(SourceAssembly))
40 if (TryGetCSpaceTypeMatch(type, out cspaceType))
42 if (type.IsValueType && !type.IsEnum)
44 SessionData.LoadMessageLogger.LogLoadMessage(Strings.Validator_OSpace_Convention_Struct(cspaceType.FullName, type.FullName), cspaceType);
49 if (TryCreateType(type, cspaceType, out ospaceType))
51 Debug.Assert(ospaceType is StructuralType || Helper.IsEnumType(ospaceType), "Only StructuralType or EnumType expected.");
53 CacheEntry.TypesInAssembly.Add(ospaceType);
54 // check for duplicates so we don't cause an ArgumentException,
55 // Mapping will do the actual error for the duplicate type later
56 if (!SessionData.CspaceToOspace.ContainsKey(cspaceType))
58 SessionData.CspaceToOspace.Add(cspaceType, ospaceType);
62 // at this point there is already a Clr Type that is structurally matched to this CSpace type, we throw exception
63 EdmType previousOSpaceType = SessionData.CspaceToOspace[cspaceType];
64 SessionData.EdmItemErrors.Add(
65 new EdmItemError(Strings.Validator_OSpace_Convention_AmbiguousClrType(cspaceType.Name, previousOSpaceType.ClrType.FullName, type.FullName), previousOSpaceType));
71 if (SessionData.TypesInLoading.Count == 0)
73 Debug.Assert(CacheEntry.ClosureAssemblies.Count == 0, "How did we get closure assemblies?");
75 // since we didn't find any types, don't lock into convention based
76 SessionData.ObjectItemAssemblyLoaderFactory = null;
81 protected override void AddToAssembliesLoaded()
83 SessionData.AssembliesLoaded.Add(SourceAssembly, CacheEntry);
86 private bool TryGetCSpaceTypeMatch(Type type, out EdmType cspaceType)
88 // brute force try and find a matching name
89 KeyValuePair<EdmType, int> pair;
90 if (SessionData.ConventionCSpaceTypeNames.TryGetValue(type.Name, out pair))
94 // we found a type match
95 cspaceType = pair.Key;
100 Debug.Assert(pair.Value > 1, "how did we get a negative count of types in the dictionary?");
101 SessionData.EdmItemErrors.Add(new EdmItemError(Strings.Validator_OSpace_Convention_MultipleTypesWithSameName(type.Name), pair.Key));
110 /// Creates a structural or enum OSpace type based on CLR type and CSpace type.
112 /// <param name="type">CLR type.</param>
113 /// <param name="cspaceType">CSpace Type</param>
114 /// <param name="newOSpaceType">OSpace type created based on CLR <paramref name="type"/> and <paramref name="cspaceType"/></param>
115 /// <returns><c>true</c> if the type was created successfully. Otherwise <c>false</c>.</returns>
116 private bool TryCreateType(Type type, EdmType cspaceType, out EdmType newOSpaceType)
118 Debug.Assert(type != null, "type != null");
119 Debug.Assert(cspaceType != null, "cspaceType != null");
120 Debug.Assert(cspaceType is StructuralType || Helper.IsEnumType(cspaceType), "Structural or enum type expected");
122 newOSpaceType = null;
124 // if one of the types is an enum while the other is not there is no match
125 if (Helper.IsEnumType(cspaceType) ^ type.IsEnum)
127 SessionData.LoadMessageLogger.LogLoadMessage(
128 Strings.Validator_OSpace_Convention_SSpaceOSpaceTypeMismatch(cspaceType.FullName, cspaceType.FullName),
133 if(Helper.IsEnumType(cspaceType))
135 return TryCreateEnumType(type, (EnumType)cspaceType, out newOSpaceType);
139 Debug.Assert(cspaceType is StructuralType);
140 return TryCreateStructuralType(type, (StructuralType)cspaceType, out newOSpaceType);
145 /// Creates a structural OSpace type based on CLR type and CSpace type.
147 /// <param name="type">CLR type.</param>
148 /// <param name="cspaceType">CSpace Type</param>
149 /// <param name="newOSpaceType">OSpace type created based on CLR <paramref name="type"/> and <paramref name="cspaceType"/></param>
150 /// <returns><c>true</c> if the type was created successfully. Otherwise <c>false</c>.</returns>
151 private bool TryCreateStructuralType(Type type, StructuralType cspaceType, out EdmType newOSpaceType)
153 Debug.Assert(type != null, "type != null");
154 Debug.Assert(cspaceType != null, "cspaceType != null");
156 List<Action> referenceResolutionListForCurrentType = new List<Action>();
157 newOSpaceType = null;
158 Debug.Assert(TypesMatchByConvention(type, cspaceType), "The types passed as parameters don't match by convention.");
160 StructuralType ospaceType;
161 if (Helper.IsEntityType(cspaceType))
163 ospaceType = new ClrEntityType(type, cspaceType.NamespaceName, cspaceType.Name);
167 Debug.Assert(Helper.IsComplexType(cspaceType), "Invalid type attribute encountered");
168 ospaceType = new ClrComplexType(type, cspaceType.NamespaceName, cspaceType.Name);
171 if (cspaceType.BaseType != null)
173 if (TypesMatchByConvention(type.BaseType, cspaceType.BaseType))
175 TrackClosure(type.BaseType);
176 referenceResolutionListForCurrentType.Add(
177 () => ospaceType.BaseType = ResolveBaseType((StructuralType)cspaceType.BaseType, type));
181 string message = Strings.Validator_OSpace_Convention_BaseTypeIncompatible(type.BaseType.FullName, type.FullName, cspaceType.BaseType.FullName);
182 SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
187 // Load the properties for this type
188 if (!TryCreateMembers(type, (StructuralType)cspaceType, ospaceType, referenceResolutionListForCurrentType))
193 // Add this to the known type map so we won't try to load it again
194 SessionData.TypesInLoading.Add(type.FullName, ospaceType);
196 // we only add the referenceResolution to the list unless we structrually matched this type
197 foreach (var referenceResolution in referenceResolutionListForCurrentType)
199 this._referenceResolutions.Add(referenceResolution);
202 newOSpaceType = ospaceType;
207 /// Creates new enum OSpace type built based on CLR <paramref name="enumType"/> and <paramref name="cspaceEnumType"/>
209 /// <param name="enumType">CLR type to create OSpace type from.</param>
210 /// <param name="cspaceEnumType">CSpace type used to get namespace and name for the newly created OSpace type.</param>
211 /// <param name="newOSpaceType">
212 /// New enum OSpace type built based on CLR <paramref name="enumType"/> and <paramref name="cspaceEnumType"/> or null
213 /// if the type could not be built.
215 /// <returns><c>true</c> if the type was built successfully.<c>false</c> otherwise.</returns>
216 private bool TryCreateEnumType(Type enumType, EnumType cspaceEnumType, out EdmType newOSpaceType)
218 Debug.Assert(enumType != null, "enumType != null");
219 Debug.Assert(enumType.IsEnum, "enum type expected");
220 Debug.Assert(cspaceEnumType != null, "cspaceEnumType != null");
221 Debug.Assert(Helper.IsEnumType(cspaceEnumType), "Enum type expected");
222 Debug.Assert(TypesMatchByConvention(enumType, cspaceEnumType), "The types passed as parameters don't match by convention.");
224 newOSpaceType = null;
226 // Check if the OSpace and CSpace enum type match
227 if (!UnderlyingEnumTypesMatch(enumType, cspaceEnumType) || !EnumMembersMatch(enumType, cspaceEnumType))
232 newOSpaceType = new ClrEnumType(enumType, cspaceEnumType.NamespaceName, cspaceEnumType.Name);
233 SessionData.TypesInLoading.Add(enumType.FullName, newOSpaceType);
239 /// Verifies whether underlying types of CLR and EDM types match
241 /// <param name="enumType">OSpace CLR enum type.</param>
242 /// <param name="cspaceEnumType">CSpace EDM enum type.</param>
243 /// <returns><c>true</c> if types match. <c>false</c> otherwise.</returns>
244 private bool UnderlyingEnumTypesMatch(Type enumType, EnumType cspaceEnumType)
246 Debug.Assert(enumType != null, "enumType != null");
247 Debug.Assert(enumType.IsEnum, "expected enum OSpace type");
248 Debug.Assert(cspaceEnumType != null, "cspaceEnumType != null");
249 Debug.Assert(Helper.IsEnumType(cspaceEnumType), "Enum type expected");
251 // Note that TryGetPrimitiveType() will return false not only for types that are not primitive
252 // but also for CLR primitive types that are valid underlying enum types in CLR but are not
253 // a valid Edm primitive types (e.g. ulong)
254 PrimitiveType underlyingEnumType;
255 if (!ClrProviderManifest.Instance.TryGetPrimitiveType(enumType.GetEnumUnderlyingType(), out underlyingEnumType))
257 SessionData.LoadMessageLogger.LogLoadMessage(
258 Strings.Validator_UnsupportedEnumUnderlyingType(enumType.GetEnumUnderlyingType().FullName),
263 else if (underlyingEnumType.PrimitiveTypeKind != cspaceEnumType.UnderlyingType.PrimitiveTypeKind)
265 SessionData.LoadMessageLogger.LogLoadMessage(
266 Strings.Validator_OSpace_Convention_NonMatchingUnderlyingTypes, cspaceEnumType);
275 /// Verifies whether enum members of CLR and EDM types match.
277 /// <param name="enumType">OSpace CLR enum type.</param>
278 /// <param name="cspaceEnumType">CSpace EDM enum type.</param>
279 /// <returns><c>true</c> if members match. <c>false</c> otherwise.</returns>
280 private bool EnumMembersMatch(Type enumType, EnumType cspaceEnumType)
282 Debug.Assert(enumType != null, "enumType != null");
283 Debug.Assert(enumType.IsEnum, "expected enum OSpace type");
284 Debug.Assert(cspaceEnumType != null, "cspaceEnumType != null");
285 Debug.Assert(Helper.IsEnumType(cspaceEnumType), "Enum type expected");
286 Debug.Assert(cspaceEnumType.UnderlyingType.ClrEquivalentType == enumType.GetEnumUnderlyingType(), "underlying types should have already been checked");
288 var enumUnderlyingType = enumType.GetEnumUnderlyingType();
290 var cspaceSortedEnumMemberEnumerator = cspaceEnumType.Members.OrderBy(m => m.Name).GetEnumerator();
291 var ospaceSortedEnumMemberNamesEnumerator = enumType.GetEnumNames().OrderBy(n => n).GetEnumerator();
293 // no checks required if edm enum type does not have any members
294 if (!cspaceSortedEnumMemberEnumerator.MoveNext())
299 while (ospaceSortedEnumMemberNamesEnumerator.MoveNext())
301 if (cspaceSortedEnumMemberEnumerator.Current.Name == ospaceSortedEnumMemberNamesEnumerator.Current &&
302 cspaceSortedEnumMemberEnumerator.Current.Value.Equals(
304 Enum.Parse(enumType, ospaceSortedEnumMemberNamesEnumerator.Current), enumUnderlyingType, CultureInfo.InvariantCulture)))
306 if (!cspaceSortedEnumMemberEnumerator.MoveNext())
313 SessionData.LoadMessageLogger.LogLoadMessage(
314 System.Data.Entity.Strings.Mapping_Enum_OCMapping_MemberMismatch(
316 cspaceSortedEnumMemberEnumerator.Current.Name,
317 cspaceSortedEnumMemberEnumerator.Current.Value,
318 cspaceEnumType.FullName), cspaceEnumType);
323 internal override void OnLevel1SessionProcessing()
325 CreateRelationships();
327 foreach (Action resolve in _referenceResolutions)
332 base.OnLevel1SessionProcessing();
335 private EdmType ResolveBaseType(StructuralType baseCSpaceType, Type type)
338 bool foundValue = SessionData.CspaceToOspace.TryGetValue(baseCSpaceType, out ospaceType);
342 SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs(
343 Strings.Validator_OSpace_Convention_BaseTypeNotLoaded(type, baseCSpaceType),
345 SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType));
348 Debug.Assert(!foundValue || ospaceType is StructuralType, "Structural type expected (if found).");
353 private bool TryCreateMembers(Type type, StructuralType cspaceType, StructuralType ospaceType, List<Action> referenceResolutionListForCurrentType)
355 BindingFlags flags = cspaceType.BaseType == null ? RootEntityPropertyReflectionBindingFlags : PropertyReflectionBindingFlags;
357 PropertyInfo[] clrProperties = type.GetProperties(flags);
359 // required properties scalar properties first
360 if (!TryFindAndCreatePrimitiveProperties(type, cspaceType, ospaceType, clrProperties))
365 if(!TryFindAndCreateEnumProperties(type, cspaceType, ospaceType, clrProperties, referenceResolutionListForCurrentType))
370 if (!TryFindComplexProperties(type, cspaceType, ospaceType, clrProperties, referenceResolutionListForCurrentType))
375 if (!TryFindNavigationProperties(type, cspaceType, ospaceType, clrProperties, referenceResolutionListForCurrentType))
383 private bool TryFindComplexProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties, List<Action> referenceResolutionListForCurrentType)
385 List<KeyValuePair<EdmProperty, PropertyInfo>> typeClosureToTrack =
386 new List<KeyValuePair<EdmProperty, PropertyInfo>>();
387 foreach (EdmProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers<EdmProperty>().Where(m => Helper.IsComplexType(m.TypeUsage.EdmType)))
389 PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => MemberMatchesByConvention(p, cspaceProperty));
390 if (clrProperty != null)
392 typeClosureToTrack.Add(
393 new KeyValuePair<EdmProperty, PropertyInfo>(
394 cspaceProperty, clrProperty));
398 string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(cspaceProperty.Name, type.FullName);
399 SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
404 foreach (var typeToTrack in typeClosureToTrack)
406 TrackClosure(typeToTrack.Value.PropertyType);
407 // prevent the lifting of these closure variables
409 var cp = typeToTrack.Key;
410 var clrp = typeToTrack.Value;
411 referenceResolutionListForCurrentType.Add(() => CreateAndAddComplexType(type, ot, cp, clrp));
417 private bool TryFindNavigationProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties, List<Action> referenceResolutionListForCurrentType)
419 List<KeyValuePair<NavigationProperty, PropertyInfo>> typeClosureToTrack =
420 new List<KeyValuePair<NavigationProperty, PropertyInfo>>();
421 foreach (NavigationProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers<NavigationProperty>())
423 PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => NonPrimitiveMemberMatchesByConvention(p, cspaceProperty));
424 if (clrProperty != null)
426 bool needsSetter = cspaceProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many;
427 if (clrProperty.CanRead && (!needsSetter || clrProperty.CanWrite))
429 typeClosureToTrack.Add(
430 new KeyValuePair<NavigationProperty, PropertyInfo>(
431 cspaceProperty, clrProperty));
436 string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(
437 cspaceProperty.Name, type.FullName);
438 SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
443 foreach (var typeToTrack in typeClosureToTrack)
445 TrackClosure(typeToTrack.Value.PropertyType);
447 // keep from lifting these closure variables
450 var cp = typeToTrack.Key;
451 var clrp = typeToTrack.Value;
453 referenceResolutionListForCurrentType.Add(() => CreateAndAddNavigationProperty(ct, ot, cp, clrp));
459 private void TrackClosure(Type type)
462 if (SourceAssembly != type.Assembly &&
463 !CacheEntry.ClosureAssemblies.Contains(type.Assembly) &&
464 !(type.IsGenericType &&
466 EntityUtil.IsAnICollection(type) || // EntityCollection<>, List<>, ICollection<>
467 type.GetGenericTypeDefinition() == typeof(System.Data.Objects.DataClasses.EntityReference<>) ||
468 type.GetGenericTypeDefinition() == typeof(System.Nullable<>)
473 CacheEntry.ClosureAssemblies.Add(type.Assembly);
476 if (type.IsGenericType)
478 foreach (Type genericArgument in type.GetGenericArguments())
480 TrackClosure(genericArgument);
485 private void CreateAndAddComplexType(Type type, StructuralType ospaceType, EdmProperty cspaceProperty, PropertyInfo clrProperty)
487 EdmType propertyType;
488 if (SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out propertyType))
490 Debug.Assert(propertyType is StructuralType, "Structural type expected.");
492 EdmProperty property = new EdmProperty(cspaceProperty.Name, TypeUsage.Create(propertyType, new FacetValues { Nullable = false }), clrProperty, type.TypeHandle);
493 ospaceType.AddMember(property);
498 SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs(
499 Strings.Validator_OSpace_Convention_MissingOSpaceType(cspaceProperty.TypeUsage.EdmType.FullName),
500 cspaceProperty.TypeUsage.EdmType);
501 SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType));
506 private void CreateAndAddNavigationProperty(StructuralType cspaceType, StructuralType ospaceType, NavigationProperty cspaceProperty, PropertyInfo clrProperty)
508 EdmType ospaceRelationship;
509 if (SessionData.CspaceToOspace.TryGetValue(cspaceProperty.RelationshipType, out ospaceRelationship))
511 Debug.Assert(ospaceRelationship is StructuralType, "Structural type expected.");
513 bool foundTarget = false;
514 EdmType targetType = null;
515 if (Helper.IsCollectionType(cspaceProperty.TypeUsage.EdmType))
518 foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)((CollectionType)cspaceProperty.TypeUsage.EdmType).TypeUsage.EdmType, out findType);
521 Debug.Assert(findType is StructuralType, "Structural type expected.");
523 targetType = findType.GetCollectionType();
529 foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out findType);
532 Debug.Assert(findType is StructuralType, "Structural type expected.");
534 targetType = findType;
539 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");
541 NavigationProperty navigationProperty = new NavigationProperty(cspaceProperty.Name, TypeUsage.Create(targetType), clrProperty);
542 navigationProperty.RelationshipType = (RelationshipType)ospaceRelationship;
544 // we can use First because o-space relationships are created directly from
545 // c-space relationship
546 navigationProperty.ToEndMember = (RelationshipEndMember)((RelationshipType)ospaceRelationship).Members.First(e => e.Name == cspaceProperty.ToEndMember.Name);
547 navigationProperty.FromEndMember = (RelationshipEndMember)((RelationshipType)ospaceRelationship).Members.First(e => e.Name == cspaceProperty.FromEndMember.Name);
548 ospaceType.AddMember(navigationProperty);
552 EntityTypeBase missingType = cspaceProperty.RelationshipType.RelationshipEndMembers.Select(e => ((RefType)e.TypeUsage.EdmType).ElementType).First(e => e != cspaceType);
554 SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs(
555 Strings.Validator_OSpace_Convention_RelationshipNotLoaded(cspaceProperty.RelationshipType.FullName, missingType.FullName),
557 SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType));
561 private bool TryFindAndCreatePrimitiveProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties)
563 foreach (EdmProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers<EdmProperty>().Where(p => Helper.IsPrimitiveType(p.TypeUsage.EdmType)))
565 PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => MemberMatchesByConvention(p, cspaceProperty));
566 if (clrProperty != null)
568 PrimitiveType propertyType;
569 if (TryGetPrimitiveType(clrProperty.PropertyType, out propertyType))
571 if (clrProperty.CanRead && clrProperty.CanWrite)
573 AddScalarMember(type, clrProperty, ospaceType, cspaceProperty, propertyType);
577 string message = Strings.Validator_OSpace_Convention_ScalarPropertyMissginGetterOrSetter(clrProperty.Name, type.FullName, type.Assembly.FullName);
578 SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
584 string message = Strings.Validator_OSpace_Convention_NonPrimitiveTypeProperty(clrProperty.Name, type.FullName, clrProperty.PropertyType.FullName);
585 SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
591 string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(cspaceProperty.Name, type.FullName);
592 SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
599 private bool TryFindAndCreateEnumProperties(Type type, StructuralType cspaceType, StructuralType ospaceType, PropertyInfo[] clrProperties, List<Action> referenceResolutionListForCurrentType)
601 var typeClosureToTrack = new List<KeyValuePair<EdmProperty, PropertyInfo>>();
603 foreach (EdmProperty cspaceProperty in cspaceType.GetDeclaredOnlyMembers<EdmProperty>().Where(p => Helper.IsEnumType(p.TypeUsage.EdmType)))
605 PropertyInfo clrProperty = clrProperties.FirstOrDefault(p => MemberMatchesByConvention(p, cspaceProperty));
606 if (clrProperty != null)
608 typeClosureToTrack.Add(new KeyValuePair<EdmProperty, PropertyInfo>(cspaceProperty, clrProperty));
612 string message = Strings.Validator_OSpace_Convention_MissingRequiredProperty(cspaceProperty.Name, type.FullName);
613 SessionData.LoadMessageLogger.LogLoadMessage(message, cspaceType);
618 foreach (var typeToTrack in typeClosureToTrack)
620 TrackClosure(typeToTrack.Value.PropertyType);
621 // prevent the lifting of these closure variables
623 var cp = typeToTrack.Key;
624 var clrp = typeToTrack.Value;
625 referenceResolutionListForCurrentType.Add(() => CreateAndAddEnumProperty(type, ot, cp, clrp));
632 /// Creates an Enum property based on <paramref name="clrProperty"/>and adds it to the parent structural type.
634 /// <param name="type">CLR type owning <paramref name="clrProperty"/>.</param>
635 /// <param name="ospaceType">OSpace type the created property will be added to.</param>
636 /// <param name="cspaceProperty">Corresponding property from CSpace.</param>
637 /// <param name="clrProperty">CLR property used to build an Enum property.</param>
638 private void CreateAndAddEnumProperty(Type type, StructuralType ospaceType, EdmProperty cspaceProperty, PropertyInfo clrProperty)
640 EdmType propertyType;
641 if (SessionData.CspaceToOspace.TryGetValue(cspaceProperty.TypeUsage.EdmType, out propertyType))
643 if (clrProperty.CanRead && clrProperty.CanWrite)
645 AddScalarMember(type, clrProperty, ospaceType, cspaceProperty, propertyType);
650 SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs(
651 Strings.Validator_OSpace_Convention_ScalarPropertyMissginGetterOrSetter(clrProperty.Name, type.FullName, type.Assembly.FullName),
652 cspaceProperty.TypeUsage.EdmType);
654 SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType));
660 SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs(
661 Strings.Validator_OSpace_Convention_MissingOSpaceType(cspaceProperty.TypeUsage.EdmType.FullName),
662 cspaceProperty.TypeUsage.EdmType);
664 SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType));
668 private void CreateRelationships()
670 if (SessionData.ConventionBasedRelationshipsAreLoaded)
676 SessionData.ConventionBasedRelationshipsAreLoaded = true;
678 // find all the relationships
679 foreach (AssociationType cspaceAssociation in SessionData.EdmItemCollection.GetItems<AssociationType>())
681 Debug.Assert(cspaceAssociation.RelationshipEndMembers.Count == 2, "Relationships are assumed to have exactly two ends");
683 if (SessionData.CspaceToOspace.ContainsKey(cspaceAssociation))
685 // don't try to load relationships that we already know about
689 EdmType[] ospaceEndTypes = new EdmType[2];
690 if (SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[0]), out ospaceEndTypes[0]) &&
691 SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[1]), out ospaceEndTypes[1]))
693 Debug.Assert(ospaceEndTypes[0] is StructuralType);
694 Debug.Assert(ospaceEndTypes[1] is StructuralType);
696 // if we can find both ends of the relationship, then create it
698 AssociationType ospaceAssociation = new AssociationType(cspaceAssociation.Name, cspaceAssociation.NamespaceName, cspaceAssociation.IsForeignKey, DataSpace.OSpace);
699 for (int i = 0; i < cspaceAssociation.RelationshipEndMembers.Count; i++)
701 EntityType ospaceEndType = (EntityType)ospaceEndTypes[i];
702 RelationshipEndMember cspaceEnd = cspaceAssociation.RelationshipEndMembers[i];
704 ospaceAssociation.AddKeyMember(new AssociationEndMember(cspaceEnd.Name, ospaceEndType.GetReferenceType(), cspaceEnd.RelationshipMultiplicity));
706 CacheEntry.TypesInAssembly.Add(ospaceAssociation);
707 SessionData.TypesInLoading.Add(ospaceAssociation.FullName, ospaceAssociation);
708 SessionData.CspaceToOspace.Add(cspaceAssociation, ospaceAssociation);
714 private static StructuralType GetRelationshipEndType(RelationshipEndMember relationshipEndMember)
716 return ((RefType)relationshipEndMember.TypeUsage.EdmType).ElementType;
719 private static bool MemberMatchesByConvention(PropertyInfo clrProperty, EdmMember cspaceMember)
721 return clrProperty.Name == cspaceMember.Name;
724 private static bool NonPrimitiveMemberMatchesByConvention(PropertyInfo clrProperty, EdmMember cspaceMember)
726 return !clrProperty.PropertyType.IsValueType && !clrProperty.PropertyType.IsAssignableFrom(typeof(string)) && clrProperty.Name == cspaceMember.Name;
729 internal static bool SessionContainsConventionParameters(ObjectItemLoadingSessionData sessionData)
731 return sessionData.EdmItemCollection != null;
734 internal static bool TypesMatchByConvention(Type type, EdmType cspaceType)
736 return type.Name == cspaceType.Name;
739 private void AddScalarMember(Type type, PropertyInfo clrProperty, StructuralType ospaceType, EdmProperty cspaceProperty, EdmType propertyType)
741 Debug.Assert(type != null, "type != null");
742 Debug.Assert(clrProperty != null, "clrProperty != null");
743 Debug.Assert(clrProperty.CanRead && clrProperty.CanWrite, "The clr property has to have a setter and a getter.");
744 Debug.Assert(ospaceType != null, "ospaceType != null");
745 Debug.Assert(cspaceProperty != null, "cspaceProperty != null");
746 Debug.Assert(propertyType != null, "propertyType != null");
747 Debug.Assert(Helper.IsScalarType(propertyType), "Property has to be primitive or enum.");
749 var cspaceType = cspaceProperty.DeclaringType;
751 bool isKeyMember = Helper.IsEntityType(cspaceType) && ((EntityType)cspaceType).KeyMemberNames.Contains(clrProperty.Name);
753 // 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)
754 bool nullableFacetValue = !isKeyMember && (!clrProperty.PropertyType.IsValueType || Nullable.GetUnderlyingType(clrProperty.PropertyType) != null);
756 EdmProperty ospaceProperty =
759 TypeUsage.Create(propertyType, new FacetValues { Nullable = nullableFacetValue }),
765 ((EntityType)ospaceType).AddKeyMember(ospaceProperty);
769 ospaceType.AddMember(ospaceProperty);
773 internal static ObjectItemAssemblyLoader Create(Assembly assembly, ObjectItemLoadingSessionData sessionData)
775 if (!ObjectItemAttributeAssemblyLoader.IsSchemaAttributePresent(assembly))
777 return new ObjectItemConventionAssemblyLoader(assembly, sessionData);
781 // we were loading in convention mode, and ran into an assembly that can't be loaded by convention
782 sessionData.EdmItemErrors.Add(new EdmItemError(Strings.Validator_OSpace_Convention_AttributeAssemblyReferenced(assembly.FullName), null));
783 return new ObjectItemNoOpAssemblyLoader(assembly, sessionData);