1 //---------------------------------------------------------------------
2 // <copyright file="Converter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System.Collections.Generic;
11 using System.Data.Common;
12 using System.Data.Objects.DataClasses;
13 using System.Diagnostics;
14 using System.Globalization;
16 using Som = System.Data.EntityModel.SchemaObjectModel;
18 namespace System.Data.Metadata.Edm
21 /// Helper Class for converting SOM objects to metadata objects
22 /// This class should go away once we have completely integrated SOM and metadata
24 internal static class Converter
28 /// Static constructor for creating FacetDescription objects that we use
32 Debug.Assert(Enum.GetUnderlyingType(typeof(ConcurrencyMode)) == typeof(int), "Please update underlying type below accordingly.");
34 // Create the enum types that we will need
35 EnumType concurrencyModeType = new EnumType(EdmProviderManifest.ConcurrencyModeFacetName,
36 EdmConstants.EdmNamespace,
37 underlyingType: PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32),
39 dataSpace: DataSpace.CSpace);
41 foreach (string name in Enum.GetNames(typeof(ConcurrencyMode)))
43 concurrencyModeType.AddMember(
46 (int)Enum.Parse(typeof(ConcurrencyMode), name, false)));
49 Debug.Assert(Enum.GetUnderlyingType(typeof(StoreGeneratedPattern)) == typeof(int), "Please update underlying type below accordingly.");
51 EnumType storeGeneratedPatternType = new EnumType(EdmProviderManifest.StoreGeneratedPatternFacetName,
52 EdmConstants.EdmNamespace,
53 underlyingType: PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32),
55 dataSpace: DataSpace.CSpace);
57 foreach (string name in Enum.GetNames(typeof(StoreGeneratedPattern)))
59 storeGeneratedPatternType.AddMember(
62 (int)Enum.Parse(typeof(StoreGeneratedPattern), name, false)));
66 // Now create the facet description objects
67 ConcurrencyModeFacet = new FacetDescription(EdmProviderManifest.ConcurrencyModeFacetName,
71 ConcurrencyMode.None);
72 StoreGeneratedPatternFacet = new FacetDescription(EdmProviderManifest.StoreGeneratedPatternFacetName,
73 storeGeneratedPatternType,
76 StoreGeneratedPattern.None);
77 CollationFacet = new FacetDescription(EdmProviderManifest.CollationFacetName,
78 MetadataItem.EdmProviderManifest.GetPrimitiveType(PrimitiveTypeKind.String),
86 internal static readonly FacetDescription ConcurrencyModeFacet;
87 internal static readonly FacetDescription StoreGeneratedPatternFacet;
88 internal static readonly FacetDescription CollationFacet;
94 /// Converts a schema from SOM into Metadata
96 /// <param name="somSchema">The SOM schema to convert</param>
97 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
98 /// <param name="itemCollection">The item collection for currently existing metadata objects</param>
99 internal static IEnumerable<GlobalItem> ConvertSchema(Som.Schema somSchema,
100 DbProviderManifest providerManifest,
101 ItemCollection itemCollection)
103 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems = new Dictionary<Som.SchemaElement, GlobalItem>();
104 ConvertSchema(somSchema, providerManifest, new ConversionCache(itemCollection), newGlobalItems);
105 return newGlobalItems.Values;
108 internal static IEnumerable<GlobalItem> ConvertSchema(IList<Som.Schema> somSchemas,
109 DbProviderManifest providerManifest,
110 ItemCollection itemCollection)
112 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems = new Dictionary<Som.SchemaElement, GlobalItem>();
113 ConversionCache conversionCache = new ConversionCache(itemCollection);
115 foreach (Som.Schema somSchema in somSchemas)
117 ConvertSchema(somSchema, providerManifest, conversionCache, newGlobalItems);
120 return newGlobalItems.Values;
123 private static void ConvertSchema(Som.Schema somSchema, DbProviderManifest providerManifest,
124 ConversionCache convertedItemCache, Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
126 List<Som.Function> funcsWithUnresolvedTypes = new List<Som.Function>();
127 foreach (Som.SchemaType element in somSchema.SchemaTypes)
129 if (null == LoadSchemaElement(element, providerManifest, convertedItemCache, newGlobalItems))
131 if (element is Som.Function)
133 funcsWithUnresolvedTypes.Add(element as Som.Function);
138 foreach (Som.SchemaEntityType element in somSchema.SchemaTypes.OfType<Som.SchemaEntityType>())
140 LoadEntityTypePhase2(element, providerManifest, convertedItemCache, newGlobalItems);
143 foreach (var function in funcsWithUnresolvedTypes)
145 if (null == LoadSchemaElement(function, providerManifest, convertedItemCache, newGlobalItems))
147 Debug.Fail("Could not load model function definition"); //this should never happen.
151 if (convertedItemCache.ItemCollection.DataSpace == DataSpace.CSpace)
153 EdmItemCollection edmCollection = (EdmItemCollection)convertedItemCache.ItemCollection;
154 edmCollection.EdmVersion = somSchema.SchemaVersion;
158 Debug.Assert(convertedItemCache.ItemCollection.DataSpace == DataSpace.SSpace, "Did you add a new space?");
159 // when converting the ProviderManifest, the DataSpace is SSpace, but the ItemCollection is EmptyItemCollection,
160 // not StoreItemCollection
161 StoreItemCollection storeCollection = convertedItemCache.ItemCollection as StoreItemCollection;
162 if (storeCollection != null)
164 storeCollection.StoreSchemaVersion = somSchema.SchemaVersion;
170 /// Loads a schema element
172 /// <param name="element">The SOM element to process</param>
173 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
174 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
175 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
176 /// <returns>The item resulting from the load</returns>
177 internal static MetadataItem LoadSchemaElement(Som.SchemaType element,
178 DbProviderManifest providerManifest,
179 ConversionCache convertedItemCache,
180 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
182 Debug.Assert(providerManifest != null, "This will make the dataspace to be default SSpace");
183 // Try to fetch from the collection first
186 Debug.Assert(!convertedItemCache.ItemCollection.TryGetValue(element.FQName, false, out item), "Som should have checked for duplicate items");
188 // Try to fetch in our collection of new GlobalItems
189 if (newGlobalItems.TryGetValue(element, out item))
194 Som.EntityContainer entityContainer = element as Som.EntityContainer;
195 // Perform different conversion depending on the type of the SOM object
196 if (entityContainer != null)
198 item = ConvertToEntityContainer(entityContainer,
203 else if (element is Som.SchemaEntityType)
205 item = ConvertToEntityType((Som.SchemaEntityType)element,
210 else if (element is Som.Relationship)
212 item = ConvertToAssociationType((Som.Relationship)element,
217 else if (element is Som.SchemaComplexType)
219 item = ConvertToComplexType((Som.SchemaComplexType)element,
224 else if (element is Som.Function)
226 item = ConvertToFunction((Som.Function)element, providerManifest,
227 convertedItemCache, null, newGlobalItems);
229 else if (element is Som.SchemaEnumType)
231 item = ConvertToEnumType((Som.SchemaEnumType)element, newGlobalItems);
235 // the only type we don't handle is the ProviderManifest TypeElement
236 // if it is anything else, it is probably a mistake
237 Debug.Assert(element is Som.TypeElement &&
238 element.Schema.DataModel == Som.SchemaDataModelOption.ProviderManifestModel,
239 "Unknown Type in somschema");
247 /// Converts an entity container from SOM to metadata
249 /// <param name="element">The SOM element to process</param>
250 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
251 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
252 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
253 /// <returns>The entity container object resulting from the convert</returns>
254 private static EntityContainer ConvertToEntityContainer(Som.EntityContainer element,
255 DbProviderManifest providerManifest,
256 ConversionCache convertedItemCache,
257 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
259 // Creating a new entity container object and populate with converted entity set objects
260 EntityContainer entityContainer = new EntityContainer(element.Name, GetDataSpace(providerManifest));
261 newGlobalItems.Add(element, entityContainer);
263 foreach (Som.EntityContainerEntitySet entitySet in element.EntitySets)
265 entityContainer.AddEntitySetBase(ConvertToEntitySet(entitySet,
266 entityContainer.Name,
272 // Populate with converted relationship set objects
273 foreach (Som.EntityContainerRelationshipSet relationshipSet in element.RelationshipSets)
275 Debug.Assert(relationshipSet.Relationship.RelationshipKind == RelationshipKind.Association,
276 "We do not support containment set");
278 entityContainer.AddEntitySetBase(ConvertToAssociationSet(relationshipSet,
285 // Populate with converted function imports
286 foreach (Som.Function functionImport in element.FunctionImports)
288 entityContainer.AddFunctionImport(ConvertToFunction(functionImport,
289 providerManifest, convertedItemCache, entityContainer, newGlobalItems));
292 // Extract the optional Documentation
293 if (element.Documentation != null)
295 entityContainer.Documentation = ConvertToDocumentation(element.Documentation);
298 AddOtherContent(element, entityContainer);
300 return entityContainer;
304 /// Converts an entity type from SOM to metadata
306 /// This method should only build the internally contained and vertical part of the EntityType (keys, properties, and base types) but not
307 /// sideways parts (NavigationProperties) that go between types or we risk trying to access and EntityTypes keys, from the referential constraint,
308 /// before the base type, which has the keys, is setup yet.
310 /// <param name="element">The SOM element to process</param>
311 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
312 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
313 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
314 /// <returns>The entity type object resulting from the convert</returns>
315 private static EntityType ConvertToEntityType(Som.SchemaEntityType element,
316 DbProviderManifest providerManifest,
317 ConversionCache convertedItemCache,
318 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
320 string[] keyMembers = null;
321 // Check if this type has keys
322 if (element.DeclaredKeyProperties.Count != 0)
324 keyMembers = new string[element.DeclaredKeyProperties.Count];
325 for (int i = 0; i < keyMembers.Length; i++)
327 //Add the name of the key property to the list of
329 keyMembers[i] = (element.DeclaredKeyProperties[i].Property.Name);
333 EdmProperty[] properties = new EdmProperty[element.Properties.Count];
336 foreach (Som.StructuredProperty somProperty in element.Properties)
338 properties[index++] = ConvertToProperty(somProperty,
344 EntityType entityType = new EntityType(element.Name,
346 GetDataSpace(providerManifest),
350 if (element.BaseType != null)
352 entityType.BaseType = (EdmType)(LoadSchemaElement(element.BaseType,
358 // set the abstract and sealed type values for the entity type
359 entityType.Abstract = element.IsAbstract;
360 // Extract the optional Documentation
361 if (element.Documentation != null)
363 entityType.Documentation = ConvertToDocumentation(element.Documentation);
365 AddOtherContent(element, entityType);
366 newGlobalItems.Add(element, entityType);
370 private static void LoadEntityTypePhase2(Som.SchemaEntityType element,
371 DbProviderManifest providerManifest,
372 ConversionCache convertedItemCache,
373 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
375 EntityType entityType = (EntityType)newGlobalItems[element];
377 // Since Navigation properties are internal and not part of member collection, we
378 // need to initialize the base class first before we start adding the navigation property
379 // this will ensure that all the base navigation properties are initialized
380 foreach (Som.NavigationProperty somNavigationProperty in element.NavigationProperties)
382 entityType.AddMember(ConvertToNavigationProperty(entityType,
383 somNavigationProperty,
391 /// Converts an complex type from SOM to metadata
393 /// <param name="element">The SOM element to process</param>
394 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
395 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
396 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
397 /// <returns>The complex type object resulting from the convert</returns>
398 private static ComplexType ConvertToComplexType(Som.SchemaComplexType element,
399 DbProviderManifest providerManifest,
400 ConversionCache convertedItemCache,
401 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
403 ComplexType complexType = new ComplexType(element.Name,
405 GetDataSpace(providerManifest));
406 newGlobalItems.Add(element, complexType);
408 foreach (Som.StructuredProperty somProperty in element.Properties)
410 complexType.AddMember(ConvertToProperty(somProperty,
416 // set the abstract and sealed type values for the entity type
417 complexType.Abstract = element.IsAbstract;
419 if (element.BaseType != null)
421 complexType.BaseType = (EdmType)(LoadSchemaElement(element.BaseType,
427 // Extract the optional Documentation
428 if (element.Documentation != null)
430 complexType.Documentation = ConvertToDocumentation(element.Documentation);
432 AddOtherContent(element, complexType);
438 /// Converts an association type from SOM to metadata
440 /// <param name="element">The SOM element to process</param>
441 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
442 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
443 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
444 /// <returns>The association type object resulting from the convert</returns>
445 private static AssociationType ConvertToAssociationType(Som.Relationship element,
446 DbProviderManifest providerManifest,
447 ConversionCache convertedItemCache,
448 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
450 Debug.Assert(element.RelationshipKind == RelationshipKind.Association);
452 AssociationType associationType = new AssociationType(element.Name,
454 element.IsForeignKey,
455 GetDataSpace(providerManifest));
456 newGlobalItems.Add(element, associationType);
458 foreach (Som.RelationshipEnd end in element.Ends)
460 Som.SchemaType entityTypeElement = end.Type;
461 EntityType endEntityType = (EntityType)LoadSchemaElement(entityTypeElement,
466 AssociationEndMember endMember = InitializeAssociationEndMember(associationType, end, endEntityType);
467 AddOtherContent(end, endMember);
468 // Loop through and convert the operations
469 foreach (Som.OnOperation operation in end.Operations)
471 // Process only the ones that we recognize
472 if (operation.Operation != Som.Operation.Delete)
477 // Determine the action for this operation
478 OperationAction action = OperationAction.None;
479 switch (operation.Action)
481 case Som.Action.Cascade:
482 action = OperationAction.Cascade;
484 case Som.Action.None:
485 action = OperationAction.None;
488 Debug.Fail("Operation action not supported.");
491 endMember.DeleteBehavior = action;
494 // Extract optional Documentation from the end element
495 if (end.Documentation != null)
497 endMember.Documentation = ConvertToDocumentation(end.Documentation);
501 Debug.Assert(associationType.ReferentialConstraints.Count == 0, "This must never have been initialized");
503 for (int i = 0; i < element.Constraints.Count; i++)
505 Som.ReferentialConstraint constraint = element.Constraints[i];
506 AssociationEndMember fromMember = (AssociationEndMember)associationType.Members[constraint.PrincipalRole.Name];
507 AssociationEndMember toMember = (AssociationEndMember)associationType.Members[constraint.DependentRole.Name];
508 EntityTypeBase fromEntityType = ((RefType)fromMember.TypeUsage.EdmType).ElementType;
509 EntityTypeBase toEntityType = ((RefType)toMember.TypeUsage.EdmType).ElementType;
511 ReferentialConstraint referentialConstraint = new ReferentialConstraint(fromMember, toMember,
512 GetProperties(fromEntityType, constraint.PrincipalRole.RoleProperties),
513 GetProperties(toEntityType, constraint.DependentRole.RoleProperties));
515 // Attach the optional Documentation
516 if (constraint.Documentation != null)
517 referentialConstraint.Documentation = ConvertToDocumentation(constraint.Documentation);
518 if (constraint.PrincipalRole.Documentation != null)
519 referentialConstraint.FromRole.Documentation = ConvertToDocumentation(constraint.PrincipalRole.Documentation);
520 if (constraint.DependentRole.Documentation != null)
521 referentialConstraint.ToRole.Documentation = ConvertToDocumentation(constraint.DependentRole.Documentation);
524 associationType.AddReferentialConstraint(referentialConstraint);
525 AddOtherContent(element.Constraints[i], referentialConstraint);
529 // Extract the optional Documentation
530 if (element.Documentation != null)
532 associationType.Documentation = ConvertToDocumentation(element.Documentation);
534 AddOtherContent(element, associationType);
536 return associationType;
540 /// Initialize the end member if its not initialized already
542 /// <param name="associationType"></param>
543 /// <param name="end"></param>
544 /// <param name="endMemberType"></param>
545 private static AssociationEndMember InitializeAssociationEndMember(AssociationType associationType, Som.IRelationshipEnd end,
546 EntityType endMemberType)
548 AssociationEndMember associationEnd;
551 // make sure that the end is not initialized as of yet
552 if (!associationType.Members.TryGetValue(end.Name, false/*ignoreCase*/, out member))
554 // Create the end member and add the operations
555 associationEnd = new AssociationEndMember(end.Name,
556 endMemberType.GetReferenceType(),
557 end.Multiplicity.Value);
558 associationType.AddKeyMember(associationEnd);
562 associationEnd = (AssociationEndMember)member;
565 //Extract the optional Documentation
566 Som.RelationshipEnd relationshipEnd = end as Som.RelationshipEnd;
568 if (relationshipEnd != null && (relationshipEnd.Documentation != null))
570 associationEnd.Documentation = ConvertToDocumentation(relationshipEnd.Documentation);
573 return associationEnd;
576 private static EdmProperty[] GetProperties(EntityTypeBase entityType, IList<Som.PropertyRefElement> properties)
578 Debug.Assert(properties.Count != 0);
579 EdmProperty[] result = new EdmProperty[properties.Count];
581 for (int i = 0; i < properties.Count; i++)
583 result[i] = (EdmProperty)entityType.Members[properties[i].Name];
590 private static void AddOtherContent(Som.SchemaElement element, MetadataItem item)
592 if (element.OtherContent.Count > 0)
594 item.AddMetadataProperties(element.OtherContent);
599 /// Converts an entity set from SOM to metadata
601 /// <param name="set">The SOM element to process</param>
602 /// <param name="containerName">the name of the container this will be added to</param>
603 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
604 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
605 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
606 /// <returns>The entity set object resulting from the convert</returns>
607 private static EntitySet ConvertToEntitySet(Som.EntityContainerEntitySet set,
608 string containerName,
609 DbProviderManifest providerManifest,
610 ConversionCache convertedItemCache,
611 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
613 EntitySet entitySet = new EntitySet(set.Name, set.DbSchema, set.Table, set.DefiningQuery,
614 (EntityType)LoadSchemaElement(set.EntityType,
619 // Extract the optional Documentation
620 if (set.Documentation != null)
622 entitySet.Documentation = ConvertToDocumentation(set.Documentation);
624 AddOtherContent(set, entitySet);
630 /// Converts an entity set from SOM to metadata
632 /// <param name="set">The SOM element to process</param>
633 /// <param name="container"></param>
634 /// <returns>The entity set object resulting from the convert</returns>
635 private static EntitySet GetEntitySet(Som.EntityContainerEntitySet set, EntityContainer container)
637 return container.GetEntitySetByName(set.Name, false);
641 /// Converts an association set from SOM to metadata
643 /// <param name="relationshipSet">The SOM element to process</param>
644 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
645 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
646 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
647 /// <param name="container"></param>
648 /// <returns>The association set object resulting from the convert</returns>
649 private static AssociationSet ConvertToAssociationSet(Som.EntityContainerRelationshipSet relationshipSet,
650 DbProviderManifest providerManifest,
651 ConversionCache convertedItemCache,
652 EntityContainer container,
653 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
655 Debug.Assert(relationshipSet.Relationship.RelationshipKind == RelationshipKind.Association);
657 AssociationType associationType = (AssociationType)LoadSchemaElement((Som.SchemaType)relationshipSet.Relationship,
662 AssociationSet associationSet = new AssociationSet(relationshipSet.Name, associationType);
664 foreach (Som.EntityContainerRelationshipSetEnd end in relationshipSet.Ends)
666 //-- need the EntityType for the end
667 EntityType endEntityType = (EntityType)LoadSchemaElement(end.EntitySet.EntityType,
671 //-- need to get the end member
672 AssociationEndMember endMember = (AssociationEndMember)associationType.Members[end.Name];
674 AssociationSetEnd associationSetEnd = new AssociationSetEnd(GetEntitySet(end.EntitySet, container),
678 AddOtherContent(end, associationSetEnd);
679 associationSet.AddAssociationSetEnd(associationSetEnd);
681 // Extract optional Documentation from the end element
682 if (end.Documentation != null)
684 associationSetEnd.Documentation = ConvertToDocumentation(end.Documentation);
688 // Extract the optional Documentation
689 if (relationshipSet.Documentation != null)
691 associationSet.Documentation = ConvertToDocumentation(relationshipSet.Documentation);
693 AddOtherContent(relationshipSet, associationSet);
695 return associationSet;
699 /// Converts a property from SOM to metadata
701 /// <param name="somProperty">The SOM element to process</param>
702 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
703 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
704 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
705 /// <returns>The property object resulting from the convert</returns>
706 private static EdmProperty ConvertToProperty(Som.StructuredProperty somProperty,
707 DbProviderManifest providerManifest,
708 ConversionCache convertedItemCache,
709 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
711 EdmProperty property;
713 // Get the appropriate type object for this type, for primitive and enum types, get the facet values for the type
714 // property as a type usage object as well
715 TypeUsage typeUsage = null;
717 Som.ScalarType scalarType = somProperty.Type as Som.ScalarType;
719 if (scalarType != null && somProperty.Schema.DataModel != Som.SchemaDataModelOption.EntityDataModel)
722 typeUsage = somProperty.TypeUsage;
723 UpdateSentinelValuesInFacets(ref typeUsage);
727 EdmType propertyType;
729 if (scalarType != null)
731 Debug.Assert(somProperty.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType);
732 // try to get the instance of the primitive type from the item collection so that it back pointer is set.
733 propertyType = convertedItemCache.ItemCollection.GetItem<PrimitiveType>(somProperty.TypeUsage.EdmType.FullName);
737 propertyType = (EdmType)LoadSchemaElement(somProperty.Type, providerManifest, convertedItemCache, newGlobalItems);
740 if (somProperty.CollectionKind != CollectionKind.None)
742 typeUsage = TypeUsage.Create(new CollectionType(propertyType));
746 Som.SchemaEnumType enumType = scalarType == null ? somProperty.Type as Som.SchemaEnumType : null;
747 typeUsage = TypeUsage.Create(propertyType);
748 if (enumType != null)
750 somProperty.EnsureEnumTypeFacets(convertedItemCache, newGlobalItems);
753 if (somProperty.TypeUsage != null)
755 ApplyTypePropertyFacets(somProperty.TypeUsage, ref typeUsage);
760 PopulateGeneralFacets(somProperty, providerManifest, ref typeUsage);
761 property = new EdmProperty(somProperty.Name, typeUsage);
763 // Extract the optional Documentation
764 if (somProperty.Documentation != null)
766 property.Documentation = ConvertToDocumentation(somProperty.Documentation);
768 AddOtherContent(somProperty, property);
774 /// Converts a navigation property from SOM to metadata
776 /// <param name="declaringEntityType">entity type on which this navigation property was declared</param>
777 /// <param name="somNavigationProperty">The SOM element to process</param>
778 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
779 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
780 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
781 /// <returns>The property object resulting from the convert</returns>
782 private static NavigationProperty ConvertToNavigationProperty(EntityType declaringEntityType,
783 Som.NavigationProperty somNavigationProperty,
784 DbProviderManifest providerManifest,
785 ConversionCache convertedItemCache,
786 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
788 // Navigation properties cannot be primitive types, so we can ignore the possibility of having primitive type
790 EntityType toEndEntityType = (EntityType)LoadSchemaElement(somNavigationProperty.Type,
795 EdmType edmType = toEndEntityType;
797 // Also load the relationship Type that this navigation property represents
798 AssociationType relationshipType = (AssociationType)LoadSchemaElement((Som.Relationship)somNavigationProperty.Relationship,
799 providerManifest, convertedItemCache, newGlobalItems);
801 Som.IRelationshipEnd somRelationshipEnd = null;
802 somNavigationProperty.Relationship.TryGetEnd(somNavigationProperty.ToEnd.Name, out somRelationshipEnd);
803 if (somRelationshipEnd.Multiplicity == RelationshipMultiplicity.Many)
805 edmType = toEndEntityType.GetCollectionType();
809 Debug.Assert(somRelationshipEnd.Multiplicity != RelationshipMultiplicity.Many);
810 edmType = toEndEntityType;
814 if (somRelationshipEnd.Multiplicity == RelationshipMultiplicity.One)
816 typeUsage = TypeUsage.Create(edmType,
817 new FacetValues { Nullable = false });
821 typeUsage = TypeUsage.Create(edmType);
825 // We need to make sure that both the ends of the relationtype are initialized. If there are not, then we should
826 // initialize them here
827 InitializeAssociationEndMember(relationshipType, somNavigationProperty.ToEnd, toEndEntityType);
828 InitializeAssociationEndMember(relationshipType, somNavigationProperty.FromEnd, declaringEntityType);
830 // The type of the navigation property must be a ref or collection depending on which end they belong to
831 NavigationProperty navigationProperty = new NavigationProperty(somNavigationProperty.Name, typeUsage);
832 navigationProperty.RelationshipType = relationshipType;
833 navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[somNavigationProperty.ToEnd.Name];
834 navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[somNavigationProperty.FromEnd.Name];
836 // Extract the optional Documentation
837 if (somNavigationProperty.Documentation != null)
839 navigationProperty.Documentation = ConvertToDocumentation(somNavigationProperty.Documentation);
841 AddOtherContent(somNavigationProperty, navigationProperty);
843 return navigationProperty;
847 /// Converts a function from SOM to metadata
849 /// <param name="somFunction">The SOM element to process</param>
850 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
851 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
852 /// <param name="functionImportEntityContainer">For function imports, the entity container including the function declaration</param>
853 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
854 /// <returns>The function object resulting from the convert</returns>
855 private static EdmFunction ConvertToFunction(Som.Function somFunction,
856 DbProviderManifest providerManifest,
857 ConversionCache convertedItemCache,
858 EntityContainer functionImportEntityContainer,
859 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
861 // If we already have it, don't bother converting
862 GlobalItem globalItem = null;
864 // if we are converted the function import, we need not check the global items collection,
865 // since the function imports are local to the entity container
866 if (!somFunction.IsFunctionImport && newGlobalItems.TryGetValue(somFunction, out globalItem))
868 return (EdmFunction)globalItem;
871 bool areConvertingForProviderManifest = somFunction.Schema.DataModel == Som.SchemaDataModelOption.ProviderManifestModel;
872 List<FunctionParameter> returnParameters = new List<FunctionParameter>();
873 if (somFunction.ReturnTypeList != null)
876 foreach (Som.ReturnType somReturnType in somFunction.ReturnTypeList)
878 TypeUsage returnType = GetFunctionTypeUsage(somFunction is Som.ModelFunction,
882 areConvertingForProviderManifest,
884 somReturnType.CollectionKind,
885 somReturnType.IsRefType /*isRefType*/,
889 if (null != returnType)
891 // Create the return parameter object, need to set the declaring type explicitly on the return parameter
892 // because we aren't adding it to the members collection
893 string modifier = i == 0 ? string.Empty : i.ToString(System.Globalization.CultureInfo.InvariantCulture);
895 FunctionParameter returnParameter = new FunctionParameter(EdmConstants.ReturnType + modifier, returnType, ParameterMode.ReturnValue);
896 AddOtherContent(somReturnType, returnParameter);
897 returnParameters.Add(returnParameter);
905 // this case must be second to avoid calling somFunction.Type when returnTypeList has more than one element.
906 else if (somFunction.Type != null)
908 TypeUsage returnType = GetFunctionTypeUsage(somFunction is Som.ModelFunction,
912 areConvertingForProviderManifest,
914 somFunction.CollectionKind,
915 somFunction.IsReturnAttributeReftype /*isRefType*/,
919 if (null != returnType)
921 // Create the return parameter object, need to set the declaring type explicitly on the return parameter
922 // because we aren't adding it to the members collection
923 returnParameters.Add(new FunctionParameter(EdmConstants.ReturnType, returnType, ParameterMode.ReturnValue));
927 //Return type was specified but we could not find a type usage
932 string functionNamespace;
933 EntitySet[] entitySets = null;
934 if (somFunction.IsFunctionImport)
936 var somFunctionImport = (Som.FunctionImportElement)somFunction;
937 functionNamespace = somFunctionImport.Container.Name;
938 if (null != somFunctionImport.EntitySet)
940 EntityContainer entityContainer;
941 Debug.Assert(somFunctionImport.ReturnTypeList == null || somFunctionImport.ReturnTypeList.Count == 1,
942 "EntitySet cannot be specified on a FunctionImport if there are multiple ReturnType children");
944 Debug.Assert(functionImportEntityContainer != null, "functionImportEntityContainer must be specified during function import conversion");
945 entityContainer = functionImportEntityContainer;
946 entitySets = new EntitySet[] { GetEntitySet(somFunctionImport.EntitySet, entityContainer) };
948 else if (null != somFunctionImport.ReturnTypeList)
950 Debug.Assert(functionImportEntityContainer != null, "functionImportEntityContainer must be specified during function import conversion");
951 EntityContainer entityContainer = functionImportEntityContainer;
952 entitySets = somFunctionImport.ReturnTypeList
953 .Select(returnType => null != returnType.EntitySet
954 ? GetEntitySet(returnType.EntitySet, functionImportEntityContainer)
961 functionNamespace = somFunction.Namespace;
964 List<FunctionParameter> parameters = new List<FunctionParameter>();
965 foreach (Som.Parameter somParameter in somFunction.Parameters)
967 TypeUsage parameterType = GetFunctionTypeUsage(somFunction is Som.ModelFunction,
971 areConvertingForProviderManifest,
973 somParameter.CollectionKind,
974 somParameter.IsRefType,
978 if (parameterType == null)
983 FunctionParameter parameter = new FunctionParameter(somParameter.Name,
985 GetParameterMode(somParameter.ParameterDirection));
986 AddOtherContent(somParameter, parameter);
988 if (somParameter.Documentation != null)
990 parameter.Documentation = ConvertToDocumentation(somParameter.Documentation);
992 parameters.Add(parameter);
995 EdmFunction function = new EdmFunction(somFunction.Name,
997 GetDataSpace(providerManifest),
998 new EdmFunctionPayload
1000 Schema = somFunction.DbSchema,
1001 StoreFunctionName = somFunction.StoreFunctionName,
1002 CommandText = somFunction.CommandText,
1003 EntitySets = entitySets,
1004 IsAggregate = somFunction.IsAggregate,
1005 IsBuiltIn = somFunction.IsBuiltIn,
1006 IsNiladic = somFunction.IsNiladicFunction,
1007 IsComposable = somFunction.IsComposable,
1008 IsFromProviderManifest = areConvertingForProviderManifest,
1009 IsFunctionImport = somFunction.IsFunctionImport,
1010 ReturnParameters = returnParameters.ToArray(),
1011 Parameters = parameters.ToArray(),
1012 ParameterTypeSemantics = somFunction.ParameterTypeSemantics,
1015 // Add this function to new global items, only if it is not a function import
1016 if (!somFunction.IsFunctionImport)
1018 newGlobalItems.Add(somFunction, function);
1021 //Check if we already converted functions since we are loading it from
1022 //ssdl we could see functions many times.
1023 GlobalItem returnFunction = null;
1024 Debug.Assert(!convertedItemCache.ItemCollection.TryGetValue(function.Identity, false, out returnFunction),
1025 "Function duplicates must be checked by som");
1027 // Extract the optional Documentation
1028 if (somFunction.Documentation != null)
1030 function.Documentation = ConvertToDocumentation(somFunction.Documentation);
1032 AddOtherContent(somFunction, function);
1038 /// Converts SchemaEnumType instance to Metadata EnumType.
1040 /// <param name="somEnumType">SchemaEnumType to be covnerted.</param>
1041 /// <param name="newGlobalItems">Global item objects where newly created Metadata EnumType will be added.</param>
1042 /// <returns></returns>
1043 private static EnumType ConvertToEnumType(Som.SchemaEnumType somEnumType, Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
1045 Debug.Assert(somEnumType != null, "somEnumType != null");
1046 Debug.Assert(newGlobalItems != null, "newGlobalItems != null");
1047 Debug.Assert(somEnumType.UnderlyingType is Som.ScalarType, "At this point the underlying type should have already been validated and should be ScalarType");
1049 Som.ScalarType enumUnderlyingType = (Som.ScalarType)somEnumType.UnderlyingType;
1051 // note that enums don't live in SSpace so there is no need to GetDataSpace() for it.
1052 EnumType enumType = new EnumType(somEnumType.Name,
1053 somEnumType.Namespace,
1054 enumUnderlyingType.Type,
1055 somEnumType.IsFlags,
1058 Type clrEnumUnderlyingType = enumUnderlyingType.Type.ClrEquivalentType;
1060 foreach (var somEnumMember in somEnumType.EnumMembers)
1062 Debug.Assert(somEnumMember.Value != null, "value must not be null at this point");
1063 var enumMember = new EnumMember(somEnumMember.Name, Convert.ChangeType(somEnumMember.Value, clrEnumUnderlyingType, CultureInfo.InvariantCulture));
1065 if (somEnumMember.Documentation != null)
1067 enumMember.Documentation = ConvertToDocumentation(somEnumMember.Documentation);
1070 AddOtherContent(somEnumMember, enumMember);
1071 enumType.AddMember(enumMember);
1074 if (somEnumType.Documentation != null)
1076 enumType.Documentation = ConvertToDocumentation(somEnumType.Documentation);
1078 AddOtherContent(somEnumType, enumType);
1080 newGlobalItems.Add(somEnumType, enumType);
1085 /// Converts an SOM Documentation node to a metadata Documentation construct
1087 /// <param name="element">The SOM element to process</param>
1088 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
1089 /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param>
1090 /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param>
1091 /// <returns>The Documentation object resulting from the convert operation</returns>
1092 private static Documentation ConvertToDocumentation(Som.DocumentationElement element)
1094 Debug.Assert(null != element, "ConvertToDocumentation cannot be invoked with a null Som.Documentation element");
1095 return element.MetadataDocumentation;
1098 private static TypeUsage GetFunctionTypeUsage(bool isModelFunction,
1099 Som.Function somFunction,
1100 Som.FacetEnabledSchemaElement somParameter,
1101 DbProviderManifest providerManifest,
1102 bool areConvertingForProviderManifest,
1103 Som.SchemaType type,
1104 CollectionKind collectionKind,
1106 Som.SchemaElement schemaElement,
1107 ConversionCache convertedItemCache,
1108 Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems)
1110 if (null != somParameter && areConvertingForProviderManifest
1111 && somParameter.HasUserDefinedFacets)
1113 return somParameter.TypeUsage;
1118 if (isModelFunction && somParameter != null && somParameter is Som.Parameter)
1120 ((Som.Parameter)somParameter).ResolveNestedTypeNames(convertedItemCache, newGlobalItems);
1121 return somParameter.TypeUsage;
1123 else if (somParameter != null && somParameter is Som.ReturnType)
1125 ((Som.ReturnType)somParameter).ResolveNestedTypeNames(convertedItemCache, newGlobalItems);
1126 return somParameter.TypeUsage;
1135 if (!areConvertingForProviderManifest)
1137 // SOM verifies the type is either scalar, row, or entity
1138 Som.ScalarType scalarType = type as Som.ScalarType;
1139 if (null != scalarType)
1141 if (isModelFunction && somParameter != null)
1143 if (somParameter.TypeUsage == null)
1145 somParameter.ValidateAndSetTypeUsage(scalarType);
1147 return somParameter.TypeUsage;
1149 else if (isModelFunction)
1151 Som.ModelFunction modelFunction = somFunction as Som.ModelFunction;
1152 if (modelFunction.TypeUsage == null)
1154 modelFunction.ValidateAndSetTypeUsage(scalarType);
1156 return modelFunction.TypeUsage;
1158 else if (somParameter != null && somParameter.HasUserDefinedFacets && somFunction.Schema.DataModel == System.Data.EntityModel.SchemaObjectModel.SchemaDataModelOption.ProviderDataModel)
1160 somParameter.ValidateAndSetTypeUsage(scalarType);
1161 return somParameter.TypeUsage;
1165 edmType = GetPrimitiveType(scalarType, providerManifest);
1170 edmType = (EdmType)LoadSchemaElement(type,
1175 // Neither FunctionImport nor its Parameters can have facets when defined in CSDL so for enums,
1176 // since they are only a CSpace concept, we need to process facets only on model functions
1177 if (isModelFunction && type is Som.SchemaEnumType)
1179 Debug.Assert(somFunction.Schema.DataModel == Som.SchemaDataModelOption.EntityDataModel, "Enums live only in CSpace");
1181 if (somParameter != null)
1183 somParameter.ValidateAndSetTypeUsage(edmType);
1184 return somParameter.TypeUsage;
1186 else if (somFunction != null)
1188 var modelFunction = ((Som.ModelFunction)somFunction);
1189 modelFunction.ValidateAndSetTypeUsage(edmType);
1190 return modelFunction.TypeUsage;
1194 Debug.Fail("Should never get here.");
1199 else if (type is Som.TypeElement)
1201 Som.TypeElement typeElement = type as Som.TypeElement;
1202 edmType = typeElement.PrimitiveType;
1206 Som.ScalarType typeElement = type as Som.ScalarType;
1207 edmType = typeElement.Type;
1210 //Construct type usage
1212 if (collectionKind != CollectionKind.None)
1214 usage = convertedItemCache.GetCollectionTypeUsageWithNullFacets(edmType);
1218 if (edmType is EntityType && isRefType)
1220 usage = TypeUsage.Create(new RefType(edmType as EntityType));
1224 usage = convertedItemCache.GetTypeUsageWithNullFacets(edmType);
1233 /// Converts the ParameterDirection into a ParameterMode
1235 /// <param name="parameterDirection">The ParameterDirection to convert</param>
1236 /// <returns>ParameterMode</returns>
1237 private static ParameterMode GetParameterMode(ParameterDirection parameterDirection)
1240 parameterDirection == ParameterDirection.Input
1241 || parameterDirection == ParameterDirection.InputOutput
1242 || parameterDirection == ParameterDirection.Output,
1243 "Inconsistent metadata error");
1245 switch (parameterDirection)
1247 case ParameterDirection.Input:
1248 return ParameterMode.In;
1250 case ParameterDirection.Output:
1251 return ParameterMode.Out;
1253 case ParameterDirection.InputOutput:
1255 return ParameterMode.InOut;
1260 /// Apply the facet values
1262 /// <param name="sourceType">The source TypeUsage</param>
1263 /// <param name="targetType">The primitive or enum type of the target</param>
1264 private static void ApplyTypePropertyFacets(TypeUsage sourceType, ref TypeUsage targetType)
1266 Dictionary<string, Facet> newFacets = targetType.Facets.ToDictionary(f => f.Name);
1267 bool madeChange = false;
1268 foreach (Facet sourceFacet in sourceType.Facets)
1271 if (newFacets.TryGetValue(sourceFacet.Name, out targetFacet))
1273 if (!targetFacet.Description.IsConstant)
1276 newFacets[targetFacet.Name] = Facet.Create(targetFacet.Description, sourceFacet.Value);
1282 newFacets.Add(sourceFacet.Name, sourceFacet);
1288 targetType = TypeUsage.Create(targetType.EdmType, newFacets.Values);
1293 /// Populate the facets on the TypeUsage object for a property
1295 /// <param name="somProperty">The property containing the information</param>
1296 /// <param name="propertyTypeUsage">The type usage object where to populate facet</param>
1297 /// <param name="providerManifest">The provider manifest to be used for conversion</param>
1298 private static void PopulateGeneralFacets(Som.StructuredProperty somProperty,
1299 DbProviderManifest providerManifest,
1300 ref TypeUsage propertyTypeUsage)
1302 bool madeChanges = false;
1303 Dictionary<string, Facet> facets = propertyTypeUsage.Facets.ToDictionary(f => f.Name);
1304 if (!somProperty.Nullable)
1306 facets[DbProviderManifest.NullableFacetName] = Facet.Create(MetadataItem.NullableFacetDescription, false);
1310 if (somProperty.Default != null)
1312 facets[DbProviderManifest.DefaultValueFacetName] = Facet.Create(MetadataItem.DefaultValueFacetDescription, somProperty.DefaultAsObject);
1316 //This is not really a general facet
1317 //If we are dealing with a 1.1 Schema, Add a facet for CollectionKind
1318 if (somProperty.Schema.SchemaVersion == XmlConstants.EdmVersionForV1_1)
1320 Facet newFacet = Facet.Create(MetadataItem.CollectionKindFacetDescription, somProperty.CollectionKind);
1321 facets.Add(newFacet.Name, newFacet);
1327 propertyTypeUsage = TypeUsage.Create(propertyTypeUsage.EdmType, facets.Values);
1331 private static DataSpace GetDataSpace(DbProviderManifest providerManifest)
1333 Debug.Assert(providerManifest != null, "null provider manifest will be consider as SSpace");
1334 // Target attributes is for types and sets in target space.
1335 if (providerManifest is EdmProviderManifest)
1337 return DataSpace.CSpace;
1341 return DataSpace.SSpace;
1346 /// Get a primitive type when converting a CSDL schema
1348 /// <param name="scalarType">The schema type representing the primitive type</param>
1349 /// <param name="providerManifest">The provider manifest for retrieving the store types</param>
1350 private static PrimitiveType GetPrimitiveType(Som.ScalarType scalarType,
1351 DbProviderManifest providerManifest)
1353 PrimitiveType returnValue = null;
1354 string scalarTypeName = scalarType.Name;
1356 foreach (PrimitiveType primitiveType in providerManifest.GetStoreTypes())
1358 if (primitiveType.Name == scalarTypeName)
1360 returnValue = primitiveType;
1365 Debug.Assert(scalarType != null, "Som scalar type should always resolve to a primitive type");
1369 // This will update the sentinel values in the facets if required
1370 private static void UpdateSentinelValuesInFacets(ref TypeUsage typeUsage)
1372 // For string and decimal types, replace the sentinel by the max possible value
1373 PrimitiveType primitiveType = (PrimitiveType)typeUsage.EdmType;
1374 if (primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.String ||
1375 primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Binary)
1377 Facet maxLengthFacet = typeUsage.Facets[EdmProviderManifest.MaxLengthFacetName];
1378 if (Helper.IsUnboundedFacetValue(maxLengthFacet))
1380 typeUsage = typeUsage.ShallowCopy(
1383 MaxLength = Helper.GetFacet(primitiveType.FacetDescriptions,
1384 EdmProviderManifest.MaxLengthFacetName).MaxValue
1392 #region Nested types
1394 /// Cache containing item collection and type usages to support looking up and generating
1397 internal class ConversionCache
1400 internal readonly ItemCollection ItemCollection;
1401 private readonly Dictionary<EdmType, TypeUsage> _nullFacetsTypeUsage;
1402 private readonly Dictionary<EdmType, TypeUsage> _nullFacetsCollectionTypeUsage;
1405 #region Constructors
1406 internal ConversionCache(ItemCollection itemCollection)
1408 this.ItemCollection = itemCollection;
1409 this._nullFacetsTypeUsage = new Dictionary<EdmType, TypeUsage>();
1410 this._nullFacetsCollectionTypeUsage = new Dictionary<EdmType, TypeUsage>();
1416 /// Gets type usage for the given type with null facet values. Caches usage to avoid creating
1417 /// redundant type usages.
1419 internal TypeUsage GetTypeUsageWithNullFacets(EdmType edmType)
1421 // check for cached result
1423 if (_nullFacetsTypeUsage.TryGetValue(edmType, out result))
1429 result = TypeUsage.Create(edmType, FacetValues.NullFacetValues);
1432 _nullFacetsTypeUsage.Add(edmType, result);
1438 /// Gets collection type usage for the given type with null facet values. Caches usage to avoid creating
1439 /// redundant type usages.
1441 internal TypeUsage GetCollectionTypeUsageWithNullFacets(EdmType edmType)
1443 // check for cached result
1445 if (_nullFacetsCollectionTypeUsage.TryGetValue(edmType, out result))
1450 // construct collection type from cached element type
1451 TypeUsage elementTypeUsage = GetTypeUsageWithNullFacets(edmType);
1452 result = TypeUsage.Create(new CollectionType(elementTypeUsage), FacetValues.NullFacetValues);
1455 _nullFacetsCollectionTypeUsage.Add(edmType, result);