Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Mapping / StorageMappingItemLoader.cs / 1407647 / StorageMappingItemLoader.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System; using System.Diagnostics; using System.Collections; using System.IO; using System.Text; using System.Collections.Generic; using System.Xml; using System.Xml.Schema; using System.Xml.XPath; using System.Data.Metadata.Edm; using System.Data.Common.Utils; using System.Data.Mapping.ViewGeneration.Utils; using System.Collections.ObjectModel; using System.Data.EntityModel; using System.Data.Entity; using System.Globalization; using System.Linq; namespace System.Data.Mapping { using Triple = Pair>; /// /// The class loads an MSL file into memory and exposes CSMappingMetadata interfaces. /// The primary consumers of the interfaces are view genration and tools. /// ////// For Example if conceptually you could represent the CS MSL file as following /// --Mapping /// --EntityContainerMapping ( CNorthwind-->SNorthwind ) /// --EntitySetMapping /// --EntityTypeMapping /// --TableMappingFragment /// --EntityKey /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --DiscriminatorProperyMap ( constant value-->SMemberMetadata ) /// --EntityTypeMapping /// --TableMappingFragment /// --EntityKey /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --ComplexPropertyMap /// --ComplexTypeMap /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --ScalarProperyMap ( CMemberMetadata-->SMemberMetadata ) /// --DiscriminatorProperyMap ( constant value-->SMemberMetadata ) /// --AssociationSetMapping /// --AssociationTypeMapping /// --TableMappingFragment /// --EndPropertyMap /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --ScalarProperyMap ( CMemberMetadata-->SMemberMetadata ) /// --EndPropertyMap /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --EntityContainerMapping ( CMyDatabase-->SMyDatabase ) /// --CompositionSetMapping /// --CompositionTypeMapping /// --TableMappingFragment /// --ParentEntityKey /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --EntityKey /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --ScalarPropertyMap ( CMemberMetadata-->Constant value ) /// --ComplexPropertyMap /// --ComplexTypeMap /// --ScalarPropertyMap ( CMemberMetadata-->SMemberMetadata ) /// --DiscriminatorProperyMap ( constant value-->SMemberMetadata ) /// --ScalarPropertyMap ( CMemberMetadata-->Constant value ) /// The CCMappingSchemaLoader loads an Xml file that has a conceptual structure /// equivalent to the above example into in-memory data structure in a /// top-dwon approach. /// ////// The loader uses XPathNavigator to parse the XML. The advantage of using XPathNavigator /// over DOM is that it exposes the line number of the current xml content. /// This is really helpful when throwing exceptions. Another advantage is /// internal class StorageMappingItemLoader { #region Constructors ////// Public constructor. /// For Beta2 we wont support delay loading Mapping information and we would also support /// only one mapping file for workspace. /// /// /// /// /// Dictionary to keep the list of all scalar member mappings internal StorageMappingItemLoader(XmlReader reader, StorageMappingItemCollection storageMappingItemCollection, string fileName, Dictionary> scalarMemberMappings) { Debug.Assert(storageMappingItemCollection != null); Debug.Assert(scalarMemberMappings != null); this.m_storageMappingItemCollection = storageMappingItemCollection; this.m_alias = new Dictionary (StringComparer.Ordinal); //The fileName field in this class will always have absolute path since //StorageMappingItemCollection would have already done it while //preparing the filePaths if (fileName != null) { this.m_sourceLocation = fileName; } else { this.m_sourceLocation = null; } m_parsingErrors = new List (); this.m_scalarMemberMappings = scalarMemberMappings; m_containerMapping = LoadMappingItems(reader); if (m_currentNamespaceUri != null) { if (m_currentNamespaceUri == StorageMslConstructs.NamespaceUriV1) { m_version = StorageMslConstructs.MappingVersionV1; } else { Debug.Assert(m_currentNamespaceUri == StorageMslConstructs.NamespaceUriV2, "Did you add a new Namespace?"); m_version = StorageMslConstructs.MappingVersionV2; } //if (StoreItemCollection.SchemaVersion != m_version) //{ // ParsingErrors.Add(new EdmSchemaError(Strings.Mapping_StoreItemCollectionVersionIncompatible(m_version, StoreItemCollection.SchemaVersion), (int)StorageMappingErrorCode.StoreItemCollectionVersionIncompatible, EdmSchemaErrorSeverity.Error)); //} //if (EdmItemCollection.EdmVersion != m_version) //{ // ParsingErrors.Add(new EdmSchemaError(Strings.Mapping_EdmItemCollectionVersionIncompatible(m_version, EdmItemCollection.EdmVersion), (int)StorageMappingErrorCode.EdmItemCollectionVersionIncompatible, EdmSchemaErrorSeverity.Error)); //} } } #endregion #region Fields private Dictionary m_alias; //To support the aliasing mechanism provided by MSL. private StorageMappingItemCollection m_storageMappingItemCollection; //StorageMappingItemCollection private string m_sourceLocation; //location identifier for the MSL file. private List m_parsingErrors; private Dictionary > m_scalarMemberMappings; // dictionary of all the scalar member mappings - this is to validate that no property is mapped to different store types across mappings. private bool m_hasQueryViews; //set to true if any of the SetMaps have a query view so that private string m_currentNamespaceUri; private StorageEntityContainerMapping m_containerMapping; private double m_version; // cached xsd schema private static XmlSchemaSet s_mappingXmlSchema; #endregion #region Properties internal double MappingVersion { get { return m_version; } } internal IList ParsingErrors { get { return m_parsingErrors; } } internal bool HasQueryViews { get { return m_hasQueryViews; } } internal StorageEntityContainerMapping ContainerMapping { get { return m_containerMapping; } } private EdmItemCollection EdmItemCollection { get { return m_storageMappingItemCollection.EdmItemCollection; } } private StoreItemCollection StoreItemCollection { get { return m_storageMappingItemCollection.StoreItemCollection; } } #endregion #region Methods /// /// The LoadMappingSchema method loads the mapping file and initializes the /// MappingSchema that represents this mapping file. /// For Beta2 atleast, we will support only one EntityContainerMapping per mapping file. /// ///private StorageEntityContainerMapping LoadMappingItems(XmlReader innerReader) { //Using XPathDocument to load the xml file into memory. XmlReader reader = GetSchemaValidatingReader(innerReader); try { XPathDocument doc = new XPathDocument(reader); //If there were any xsd validation errors, we would have //caught these while creatring xpath document. if (m_parsingErrors.Count != 0) { //If the errors were only warnings continue, otherwise return the errors without //loading the mapping if (!MetadataHelper.CheckIfAllErrorsAreWarnings(m_parsingErrors)) { return null; } } //Create an XPathNavigator to navigate the document in a forward only manner. //The XPathNavigator can also be used to run quries through the document while still maintaining //the current position. This will be helpful in running validation rules that are not part of Schema. XPathNavigator nav = doc.CreateNavigator(); return LoadMappingItems(nav); } catch (XmlException xmlException) { //There must have been a xml parsing exception. Add the exception information //to the error list. EdmSchemaError error = new EdmSchemaError(System.Data.Entity.Strings.Mapping_InvalidMappingSchema_Parsing_1(xmlException.Message) , (int)StorageMappingErrorCode.XmlSchemaParsingError, EdmSchemaErrorSeverity.Error, m_sourceLocation, xmlException.LineNumber, xmlException.LinePosition); m_parsingErrors.Add(error); } // do not close the wrapping reader here, as doing so will close the inner reader; // see SQLBUDT 522950 for details return null; } private StorageEntityContainerMapping LoadMappingItems(XPathNavigator nav) { //XSD validation is not validating missing Root element. if (!MoveToRootElement(nav) || (nav.NodeType != XPathNodeType.Element)) { StorageMappingItemLoader.AddToSchemaErrors(Strings.Mapping_Invalid_CSRootElementMissing_0(StorageMslConstructs.NamespaceUriV1,StorageMslConstructs.NamespaceUriV2), StorageMappingErrorCode.RootMappingElementMissing, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); //There is no point in going forward if the required root element is not found return null; } StorageEntityContainerMapping entityContainerMap = LoadMappingChildNodes(nav.Clone()); //If there were any parsing errors, invalidate the entity container map and return null. if (m_parsingErrors.Count != 0) { //If all the schema errors are warnings, don't return null if (!MetadataHelper.CheckIfAllErrorsAreWarnings(m_parsingErrors)) { entityContainerMap = null; } } return entityContainerMap; } private bool MoveToRootElement(XPathNavigator nav) { if (nav.MoveToChild(StorageMslConstructs.MappingElement, StorageMslConstructs.NamespaceUriV2)) { // found v2 schema m_currentNamespaceUri = StorageMslConstructs.NamespaceUriV2; return true; } else if (nav.MoveToChild(StorageMslConstructs.MappingElement, StorageMslConstructs.NamespaceUriV1)) { m_currentNamespaceUri = StorageMslConstructs.NamespaceUriV1; return true; } //the xml namespace corresponds to neither v1 namespace nor v2 namespace return false; } /// /// The method loads the child nodes for the root Mapping node /// into the internal datastructures. /// /// ///private StorageEntityContainerMapping LoadMappingChildNodes(XPathNavigator nav) { IXmlLineInfo navLineInfo = (IXmlLineInfo)nav; bool hasContainerMapping; //If there are any Alias elements in the document, they should be the first ones. //This method can only move to the Alias element since comments, PIS etc wont have any Namespace //though they could have same name as Alias element if (nav.MoveToChild(StorageMslConstructs.AliasElement, m_currentNamespaceUri)) { //Collect all the alias elements do { m_alias.Add(StorageMappingItemLoader.GetAttributeValue(nav.Clone(), StorageMslConstructs.AliasKeyAttribute), StorageMappingItemLoader.GetAttributeValue(nav.Clone(), StorageMslConstructs.AliasValueAttribute)); } while (nav.MoveToNext(StorageMslConstructs.AliasElement, m_currentNamespaceUri)); //Now move on to the Next element that will be "EntityContainer" element hasContainerMapping = nav.MoveToNext(XPathNodeType.Element); } else { //Since there was no Alias element, move on to the Container element hasContainerMapping = nav.MoveToChild(XPathNodeType.Element); } //No container mappings found, so just return if (!hasContainerMapping) return null; //The element name can only be EntityContainerMapping element name since XSD validation should have guarneteed this. Debug.Assert(nav.LocalName == StorageMslConstructs.EntityContainerMappingElement); string entityContainerName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.CDMEntityContainerAttribute); string storageEntityContainerName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.StorageEntityContainerAttribute); bool generateUpdateViews = GetBoolAttributeValue(nav.Clone(), StorageMslConstructs.GenerateUpdateViews, true /* default is true */); StorageEntityContainerMapping entityContainerMapping; EntityContainer entityContainerType; EntityContainer storageEntityContainerType; // Now that we support partial mapping, we should first check if the entity container mapping is // already present. If its already present, we should add the new child nodes to the existing entity container mapping if (m_storageMappingItemCollection.TryGetItem ( entityContainerName, out entityContainerMapping)) { entityContainerType = entityContainerMapping.EdmEntityContainer; storageEntityContainerType = entityContainerMapping.StorageEntityContainer; // The only thing we need to make sure is that the storage entity container mapping is the same. if (storageEntityContainerName != storageEntityContainerType.Name) { AddToSchemaErrors(System.Data.Entity.Strings.StorageEntityContainerNameMismatchWhileSpecifyingPartialMapping( storageEntityContainerName, storageEntityContainerType.Name, entityContainerType.Name), StorageMappingErrorCode.StorageEntityContainerNameMismatchWhileSpecifyingPartialMapping, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } } else { //At this point we know that the EdmEntityContainer has not been //mapped already. if we do find that StorageEntityContainer //has already been mapped, return null. if (m_storageMappingItemCollection.ContainsStorageEntityContainer(storageEntityContainerName)) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_AlreadyMapped_StorageEntityContainer_1, storageEntityContainerName, StorageMappingErrorCode.AlreadyMappedStorageEntityContainer, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } //Get the CDM EntityContainer by this name from the metadata workspace. this.EdmItemCollection.TryGetEntityContainer(entityContainerName, out entityContainerType); if (entityContainerType == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_EntityContainer_1, entityContainerName, StorageMappingErrorCode.InvalidEntityContainer, m_sourceLocation, navLineInfo, m_parsingErrors); } this.StoreItemCollection.TryGetEntityContainer(storageEntityContainerName, out storageEntityContainerType); if (storageEntityContainerType == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_StorageEntityContainer_1, storageEntityContainerName, StorageMappingErrorCode.InvalidEntityContainer, m_sourceLocation, navLineInfo, m_parsingErrors); } //If the EntityContainerTypes are not found, there is no point in //continuing with the parsing. if ((entityContainerType == null) || (storageEntityContainerType == null)) { return null; } //Create an EntityContainerMapping object to hold the mapping information for this //EntityContainer. Create a MappingKey and pass it in. entityContainerMapping = new StorageEntityContainerMapping(entityContainerType, storageEntityContainerType, m_storageMappingItemCollection, generateUpdateViews /* make validate same as generateUpdateView*/, generateUpdateViews); entityContainerMapping.StartLineNumber = navLineInfo.LineNumber; entityContainerMapping.StartLinePosition = navLineInfo.LinePosition; } //Load the child nodes for the created EntityContainerMapping LoadEntityContainerMapping(nav.Clone(), entityContainerMapping, storageEntityContainerType); return entityContainerMapping; } /// /// The method loads the child nodes for the EntityContainer Mapping node /// into the internal datastructures. /// /// /// /// private void LoadEntityContainerMapping(XPathNavigator nav, StorageEntityContainerMapping entityContainerMapping, EntityContainer storageEntityContainerType) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; bool anyEntitySetMapped = false; //If there is no child node for the EntityContainerMapping Element, return. if (nav.MoveToChild(XPathNodeType.Element)) { //The valid child nodes for EntityContainerMapping node are various SetMappings( EntitySet, AssociationSet etc ). //Loop through the child nodes and lod them as children of the EntityContainerMapping object. do { switch (nav.LocalName) { case StorageMslConstructs.EntitySetMappingElement: { LoadEntitySetMapping(nav.Clone(), entityContainerMapping, storageEntityContainerType); anyEntitySetMapped = true; break; } case StorageMslConstructs.AssociationSetMappingElement: { LoadAssociationSetMapping(nav.Clone(), entityContainerMapping, storageEntityContainerType); break; } case StorageMslConstructs.FunctionImportMappingElement: { LoadFunctionImportMapping(nav.Clone(), entityContainerMapping, storageEntityContainerType); break; } default: AddToSchemaErrors(Strings.Mapping_InvalidContent_Set_Mapping_0, StorageMappingErrorCode.SetMappingExpected, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); break; } } while (nav.MoveToNext(XPathNodeType.Element)); } //If the EntityContainer contains entity sets but they are not mapped then we should add an error if (entityContainerMapping.EdmEntityContainer.BaseEntitySets.Count != 0 && !anyEntitySetMapped) { AddToSchemaErrorsWithMemberInfo(Strings.ViewGen_Missing_Sets_Mapping_0, entityContainerMapping.EdmEntityContainer.Name, StorageMappingErrorCode.EmptyContainerMapping, this.m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return; } ValidateFunctionAssociationFunctionMappingUnique(nav.Clone(), entityContainerMapping); ValidateAssociationFunctionMappingConsistent(nav.Clone(), entityContainerMapping); ValidateQueryViewsClosure(nav.Clone(), entityContainerMapping); ValidateFunctionMappingClosure(nav.Clone(), entityContainerMapping); //The fileName field in this class will always have absolute path since //StorageMappingItemCollection would have already done it while //preparing the filePaths entityContainerMapping.SourceLocation = m_sourceLocation; } ////// Validates that collocated association sets are consistently mapped for each entity set (all operations or none). In the case /// of relationships between sub-types of an entity set, ensures the relationship mapping is legal. /// /// /// private void ValidateAssociationFunctionMappingConsistent(XPathNavigator nav, StorageEntityContainerMapping entityContainerMapping) { foreach (StorageEntitySetMapping entitySetMapping in entityContainerMapping.EntitySetMaps) { if (entitySetMapping.FunctionMappings.Count > 0) { // determine the set of association sets that should be mapped for every operation SetexpectedEnds = new Set ( entitySetMapping.ImplicitlyMappedAssociationSetEnds).MakeReadOnly(); // check that each operation covers each association set foreach (StorageEntityTypeFunctionMapping entityTypeMapping in entitySetMapping.FunctionMappings) { if (null != entityTypeMapping.DeleteFunctionMapping) { ValidateAssociationFunctionMappingConsistent(nav, entitySetMapping, entityTypeMapping, entityTypeMapping.DeleteFunctionMapping, expectedEnds, StorageMslConstructs.DeleteFunctionElement); } if (null != entityTypeMapping.InsertFunctionMapping) { ValidateAssociationFunctionMappingConsistent(nav, entitySetMapping, entityTypeMapping, entityTypeMapping.InsertFunctionMapping, expectedEnds, StorageMslConstructs.InsertFunctionElement); } if (null != entityTypeMapping.UpdateFunctionMapping) { ValidateAssociationFunctionMappingConsistent(nav, entitySetMapping, entityTypeMapping, entityTypeMapping.UpdateFunctionMapping, expectedEnds, StorageMslConstructs.UpdateFunctionElement); } } } } } private void ValidateAssociationFunctionMappingConsistent(XPathNavigator nav, StorageEntitySetMapping entitySetMapping, StorageEntityTypeFunctionMapping entityTypeMapping, StorageFunctionMapping functionMapping, Set expectedEnds, string elementName) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; // check that all expected association sets are mapped for in this function mapping Set actualEnds = new Set (functionMapping.CollocatedAssociationSetEnds); actualEnds.MakeReadOnly(); // check that all required ends are present foreach (AssociationSetEnd expectedEnd in expectedEnds) { // check that the association set is required based on the entity type if (MetadataHelper.IsAssociationValidForEntityType(expectedEnd, entityTypeMapping.EntityType)) { if (!actualEnds.Contains(expectedEnd)) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_Invalid_Function_Mapping_AssociationSetNotMappedForOperation_4( entitySetMapping.Set.Name, expectedEnd.ParentAssociationSet.Name, elementName, entityTypeMapping.EntityType.FullName), StorageMappingErrorCode.InvalidFunctionMappingAssociationSetNotMappedForOperation, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } } } // check that no ends with invalid types are included foreach (AssociationSetEnd actualEnd in actualEnds) { if (!MetadataHelper.IsAssociationValidForEntityType(actualEnd, entityTypeMapping.EntityType)) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_Invalid_Function_Mapping_AssociationEndMappingInvalidForEntityType_3( entityTypeMapping.EntityType.FullName, actualEnd.ParentAssociationSet.Name, MetadataHelper.GetEntityTypeForEnd(MetadataHelper.GetOppositeEnd(actualEnd).CorrespondingAssociationEndMember).FullName), StorageMappingErrorCode.InvalidFunctionMappingAssociationEndMappingInvalidForEntityType, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } } } /// /// Validates that association sets are only mapped once. /// /// /// Container to validate private void ValidateFunctionAssociationFunctionMappingUnique(XPathNavigator nav, StorageEntityContainerMapping entityContainerMapping) { DictionarymappingCounts = new Dictionary (); // Walk through all entity set mappings foreach (StorageEntitySetMapping entitySetMapping in entityContainerMapping.EntitySetMaps) { if (entitySetMapping.FunctionMappings.Count > 0) { // Get set of association sets implicitly mapped associations to avoid double counting Set associationSets = new Set (); foreach (AssociationSetEnd end in entitySetMapping.ImplicitlyMappedAssociationSetEnds) { associationSets.Add(end.ParentAssociationSet); } foreach (EntitySetBase associationSet in associationSets) { IncrementCount(mappingCounts, associationSet); } } } // Walk through all association set mappings foreach (StorageAssociationSetMapping associationSetMapping in entityContainerMapping.RelationshipSetMaps) { if (null != associationSetMapping.FunctionMapping) { IncrementCount(mappingCounts, associationSetMapping.Set); } } // Check for redundantly mapped association sets List violationNames = new List (); foreach (KeyValuePair mappingCount in mappingCounts) { if (mappingCount.Value > 1) { violationNames.Add(mappingCount.Key.Name); } } if (0 < violationNames.Count) { // Warn the user that association sets are mapped multiple times AddToSchemaErrorsWithMemberInfo(Strings.Mapping_Invalid_Function_Mapping_AssociationSetAmbiguous_1, StringUtil.ToCommaSeparatedString(violationNames), StorageMappingErrorCode.AmbiguousFunctionMappingForAssociationSet, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); } } private static void IncrementCount (Dictionary counts, T key) { int count; if (counts.TryGetValue(key, out count)) { count++; } else { count = 1; } counts[key] = count; } /// /// Validates that all or no related extents have function mappings. If an EntitySet or an AssociationSet has a function mapping, /// then all the sets that touched the same store tableSet must also have function mappings. /// /// /// Container to validate. private void ValidateFunctionMappingClosure(XPathNavigator nav, StorageEntityContainerMapping entityContainerMapping) { // here we build a mapping between the tables and the sets, // setmapping => typemapping => mappingfragments, foreach mappingfragments we have one Tableset, // then add the tableset with setmapping to the dictionary KeyToListMapsetMappingPerTable = new KeyToListMap (EqualityComparer .Default); // Walk through all set mappings foreach (var setMapping in entityContainerMapping.AllSetMaps) { foreach (var typeMapping in setMapping.TypeMappings) { foreach (var fragment in typeMapping.MappingFragments) { setMappingPerTable.Add(fragment.TableSet, setMapping); } } } // Get set of association sets implicitly mapped associations to avoid double counting Set implicitMappedAssociationSets = new Set (); // Walk through all entity set mappings foreach (StorageEntitySetMapping entitySetMapping in entityContainerMapping.EntitySetMaps) { if (entitySetMapping.FunctionMappings.Count > 0) { foreach (AssociationSetEnd end in entitySetMapping.ImplicitlyMappedAssociationSetEnds) { implicitMappedAssociationSets.Add(end.ParentAssociationSet); } } } foreach (var table in setMappingPerTable.Keys) { // if any of the sets who touches the same table has modification function, // then all the sets that touches the same table should have modification function if (setMappingPerTable.ListForKey(table).Any(s => s.HasModificationFunctionMapping || implicitMappedAssociationSets.Any(aset=> aset == s.Set)) && setMappingPerTable.ListForKey(table).Any(s => !s.HasModificationFunctionMapping && !implicitMappedAssociationSets.Any(aset => aset == s.Set))) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_Invalid_Function_Mapping_MissingSetClosure_1, StringUtil.ToCommaSeparatedString(setMappingPerTable.ListForKey(table) .Where(s => !s.HasModificationFunctionMapping).Select(s=>s.Set.Name)), StorageMappingErrorCode.MissingSetClosureInFunctionMapping, m_sourceLocation, (IXmlLineInfo)nav , m_parsingErrors); } } } private static void ValidateClosureAmongSets(StorageEntityContainerMapping entityContainerMapping, Set sets, Set additionalSetsInClosure) { bool nodeFound; do { nodeFound = false; List newNodes = new List (); // Register entity sets dependencies for association sets foreach (EntitySetBase entitySetBase in additionalSetsInClosure) { AssociationSet associationSet = entitySetBase as AssociationSet; //Foreign Key Associations do not add to the dependancies if (associationSet != null && !associationSet.ElementType.IsForeignKey) { // add the entity sets bound to the end roles to the required list foreach (AssociationSetEnd end in associationSet.AssociationSetEnds) { if (!additionalSetsInClosure.Contains(end.EntitySet)) { newNodes.Add(end.EntitySet); } } } } // Register all association sets referencing known entity sets foreach (EntitySetBase entitySetBase in entityContainerMapping.EdmEntityContainer.BaseEntitySets) { AssociationSet associationSet = entitySetBase as AssociationSet; //Foreign Key Associations do not add to the dependancies if (associationSet != null && !associationSet.ElementType.IsForeignKey) { // check that this association set isn't already in the required set if (!additionalSetsInClosure.Contains(associationSet)) { foreach (AssociationSetEnd end in associationSet.AssociationSetEnds) { if (additionalSetsInClosure.Contains(end.EntitySet)) { // this association set must be added to the required list if // any of its ends are in that list newNodes.Add(associationSet); break; // no point adding the association set twice } } } } } if (0 < newNodes.Count) { nodeFound = true; additionalSetsInClosure.AddRange(newNodes); } } while (nodeFound); additionalSetsInClosure.Subtract(sets); } /// /// Validates that all or no related extents have query views defined. If an extent has a query view defined, then /// all related extents must also have query views. /// /// /// Container to validate. private void ValidateQueryViewsClosure(XPathNavigator nav, StorageEntityContainerMapping entityContainerMapping) { //If there is no query view defined, no need to validate if (!m_hasQueryViews) { return; } // Check that query views apply to complete subgraph by tracking which extents have query // mappings and which extents must include query views SetsetsWithQueryViews = new Set (); Set setsRequiringQueryViews = new Set (); // Walk through all set mappings foreach (StorageSetMapping setMapping in entityContainerMapping.AllSetMaps) { if (setMapping.QueryView != null) { // a function mapping exists for this entity set setsWithQueryViews.Add(setMapping.Set); } } // Initialize sets requiring function mapping with the sets that are actually function mapped setsRequiringQueryViews.AddRange(setsWithQueryViews); ValidateClosureAmongSets(entityContainerMapping, setsWithQueryViews, setsRequiringQueryViews); // Check that no required entity or association sets are missing if (0 < setsRequiringQueryViews.Count) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_Invalid_Query_Views_MissingSetClosure_1, StringUtil.ToCommaSeparatedString(setsRequiringQueryViews), StorageMappingErrorCode.MissingSetClosureInQueryViews, m_sourceLocation, (IXmlLineInfo)nav , m_parsingErrors); } } /// /// The method loads the child nodes for the EntitySet Mapping node /// into the internal datastructures. /// /// /// /// private void LoadEntitySetMapping(XPathNavigator nav, StorageEntityContainerMapping entityContainerMapping, EntityContainer storageEntityContainerType) { //Get the EntitySet name string entitySetName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.EntitySetMappingNameAttribute); //Get the EntityType name, need to parse it if the mapping information is being specified for multiple types string entityTypeName = StorageMappingItemLoader.GetAttributeValue(nav.Clone(), StorageMslConstructs.EntitySetMappingTypeNameAttribute); //Get the table name. This might be emptystring since the user can have a TableMappingFragment instead of this. string tableName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.EntitySetMappingStoreEntitySetAttribute); bool distinctFlag = GetBoolAttributeValue(nav.Clone(), StorageMslConstructs.MappingFragmentMakeColumnsDistinctAttribute, false /*default value*/); EntitySet entitySet; // First check to see if the Entity Set Mapping is already specified. It can be specified, in the same schema file later on // on a totally different file. Since we support partial mapping, we should just add mapping fragments or entity type // mappings to the existing entity set mapping StorageEntitySetMapping setMapping = (StorageEntitySetMapping)entityContainerMapping.GetEntitySetMapping(entitySetName); // Update the info about the schema element IXmlLineInfo navLineInfo = (IXmlLineInfo)nav; if (setMapping == null) { //Try to find the EntitySet with the given name in the EntityContainer. if (!entityContainerMapping.EdmEntityContainer.TryGetEntitySetByName(entitySetName, /*ignoreCase*/ false, out entitySet)) { //If no EntitySet with the given name exists, than add a schema error and return AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Entity_Set_1, entitySetName, StorageMappingErrorCode.InvalidEntitySet, m_sourceLocation, navLineInfo, m_parsingErrors); //There is no point in continuing the loding of this EntitySetMapping if the EntitySet is not found return; } //Create the EntitySet Mapping which contains the mapping information for EntitySetMap. setMapping = new StorageEntitySetMapping(entitySet, entityContainerMapping); } else { entitySet = (EntitySet)setMapping.Set; } //Set the Start Line Information on Fragment setMapping.StartLineNumber = navLineInfo.LineNumber; setMapping.StartLinePosition = navLineInfo.LinePosition; entityContainerMapping.AddEntitySetMapping(setMapping); //If the TypeName was not specified as an attribute, than an EntityTypeMapping element should be present if (String.IsNullOrEmpty(entityTypeName)) { if (nav.MoveToChild(XPathNodeType.Element)) { do { switch (nav.LocalName) { case StorageMslConstructs.EntityTypeMappingElement: { //TableName could also be specified on EntityTypeMapping element tableName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.EntityTypeMappingStoreEntitySetAttribute); //Load the EntityTypeMapping into memory. LoadEntityTypeMapping(nav.Clone(), setMapping, tableName, storageEntityContainerType, false /*No distinct flag so far*/, entityContainerMapping.GenerateUpdateViews); break; } case StorageMslConstructs.QueryViewElement: { if (!(String.IsNullOrEmpty(tableName))) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_TableName_QueryView_1, entitySetName, StorageMappingErrorCode.TableNameAttributeWithQueryView, m_sourceLocation, navLineInfo, m_parsingErrors); return; } //Load the Query View into the set mapping, //if you get an error, return immediately since //you go on, you could be giving lot of dubious errors if(!LoadQueryView(nav.Clone(), setMapping)) { return; } break; } default: AddToSchemaErrors(Strings.Mapping_InvalidContent_TypeMapping_QueryView, StorageMappingErrorCode.InvalidContent, m_sourceLocation, navLineInfo, m_parsingErrors); break; } } while (nav.MoveToNext(XPathNodeType.Element)); } } else { //Load the EntityTypeMapping into memory. LoadEntityTypeMapping(nav.Clone(), setMapping, tableName, storageEntityContainerType, distinctFlag, entityContainerMapping.GenerateUpdateViews); } ValidateAllEntityTypesHaveFunctionMapping(nav.Clone(), setMapping); //Add a schema error if the set mapping has no content if (setMapping.HasNoContent) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Emtpty_SetMap_1, entitySet.Name, StorageMappingErrorCode.EmptySetMapping, m_sourceLocation, navLineInfo, m_parsingErrors); } } // Ensure if any type has a function mapping, all types have function mappings private void ValidateAllEntityTypesHaveFunctionMapping(XPathNavigator nav, StorageEntitySetMapping setMapping) { SetfunctionMappedTypes = new Set (); foreach (StorageEntityTypeFunctionMapping functionMapping in setMapping.FunctionMappings) { functionMappedTypes.Add(functionMapping.EntityType); } if (0 < functionMappedTypes.Count) { Set unmappedTypes = new Set (MetadataHelper.GetTypeAndSubtypesOf(setMapping.Set.ElementType, EdmItemCollection, false /*includeAbstractTypes*/)); unmappedTypes.Subtract(functionMappedTypes); // Remove abstract types Set abstractTypes = new Set (); foreach (EntityType unmappedType in unmappedTypes) { if (unmappedType.Abstract) { abstractTypes.Add(unmappedType); } } unmappedTypes.Subtract(abstractTypes); // See if there are any remaining entity types requiring function mapping if (0 < unmappedTypes.Count) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_Invalid_Function_Mapping_MissingEntityType_1, StringUtil.ToCommaSeparatedString(unmappedTypes), StorageMappingErrorCode.MissingFunctionMappingForEntityType, m_sourceLocation, (IXmlLineInfo)nav , m_parsingErrors); } } } private bool TryParseEntityTypeAttribute(XPathNavigator nav, EntityType rootEntityType, Func typeNotAssignableMessage, out Set isOfTypeEntityTypes, out Set entityTypes) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; string entityTypeAttribute = GetAttributeValue(nav.Clone(), StorageMslConstructs.EntitySetMappingTypeNameAttribute); isOfTypeEntityTypes = new Set (); entityTypes = new Set (); // get components of type declaration var entityTypeNames = entityTypeAttribute.Split(StorageMslConstructs.TypeNameSperator).Select(s => s.Trim()); // figure out each component foreach (var name in entityTypeNames) { bool isTypeOf = name.StartsWith(StorageMslConstructs.IsTypeOf, StringComparison.Ordinal); string entityTypeName; if (isTypeOf) { // get entityTypeName of OfType(entityTypeName) if (!name.EndsWith(StorageMslConstructs.IsTypeOfTerminal, StringComparison.Ordinal)) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_InvalidContent_IsTypeOfNotTerminated, StorageMappingErrorCode.InvalidEntityType, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); // No point in continuing with an error in the entitytype name return false; } entityTypeName = name.Substring(StorageMslConstructs.IsTypeOf.Length); entityTypeName = entityTypeName.Substring(0, entityTypeName.Length - StorageMslConstructs.IsTypeOfTerminal.Length).Trim(); } else { entityTypeName = name; } // resolve aliases entityTypeName = GetAliasResolvedValue(entityTypeName); EntityType entityType; if (!this.EdmItemCollection.TryGetItem (entityTypeName, out entityType)) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Entity_Type_1, entityTypeName, StorageMappingErrorCode.InvalidEntityType, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); // No point in continuing with an error in the entitytype name return false; } if (!(Helper.IsAssignableFrom(rootEntityType, entityType))) { IXmlLineInfo lineInfo = xmlLineInfoNav; AddToSchemaErrorWithMessage( typeNotAssignableMessage(entityType), StorageMappingErrorCode.InvalidEntityType, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); //no point in continuing with an error in the entitytype name return false; } // Using TypeOf construct on an abstract type that does not have // any concrete descendants is not allowed if (entityType.Abstract) { if (isTypeOf) { IEnumerable typeAndSubTypes = MetadataHelper.GetTypeAndSubtypesOf(entityType, EdmItemCollection, false /*includeAbstractTypes*/); if (!typeAndSubTypes.GetEnumerator().MoveNext()) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_AbstractEntity_IsOfType_1, entityType.FullName, StorageMappingErrorCode.MappingForAbstractEntityType, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return false; } } else { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_AbstractEntity_Type_1, entityType.FullName, StorageMappingErrorCode.MappingForAbstractEntityType, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return false; } } // Add type to set if (isTypeOf) { isOfTypeEntityTypes.Add(entityType); } else { entityTypes.Add(entityType); } } // No failures return true; } /// /// The method loads the child nodes for the EntityType Mapping node /// into the internal datastructures. /// /// /// /// /// private void LoadEntityTypeMapping(XPathNavigator nav, StorageEntitySetMapping entitySetMapping, string tableName, EntityContainer storageEntityContainerType, bool distinctFlagAboveType, bool generateUpdateViews) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; //Create an EntityTypeMapping to hold the information for EntityType mapping. StorageEntityTypeMapping entityTypeMapping = new StorageEntityTypeMapping(entitySetMapping); //Get entity types SetentityTypes; Set isOfTypeEntityTypes; EntityType rootEntityType = (EntityType)entitySetMapping.Set.ElementType; if (!TryParseEntityTypeAttribute(nav.Clone(), rootEntityType, e => System.Data.Entity.Strings.Mapping_InvalidContent_Entity_Type_For_Entity_Set_3(e.FullName, rootEntityType.FullName, entitySetMapping.Set.Name), out isOfTypeEntityTypes, out entityTypes)) { // Return if we cannot parse entity types return; } // Register all mapped types foreach (EntityType entityType in entityTypes) { entityTypeMapping.AddType(entityType); } foreach (EntityType isOfTypeEntityType in isOfTypeEntityTypes) { entityTypeMapping.AddIsOfType(isOfTypeEntityType); } //If the table name was not specified on the EntitySetMapping element nor the EntityTypeMapping element //than a table mapping fragment element should be present //Loop through the TableMappingFragment elements and add them to EntityTypeMappings if (String.IsNullOrEmpty(tableName)) { if (!nav.MoveToChild(XPathNodeType.Element)) return; do { if (nav.LocalName == StorageMslConstructs.ModificationFunctionMappingElement) { entitySetMapping.HasModificationFunctionMapping = true; LoadEntityTypeFunctionMapping(nav.Clone(), entitySetMapping, entityTypeMapping); } else if (nav.LocalName != StorageMslConstructs.MappingFragmentElement) { AddToSchemaErrors(Strings.Mapping_InvalidContent_Table_Expected_0, StorageMappingErrorCode.TableMappingFragmentExpected, m_sourceLocation, xmlLineInfoNav , m_parsingErrors); } else { bool distinctFlag = GetBoolAttributeValue(nav.Clone(), StorageMslConstructs.MappingFragmentMakeColumnsDistinctAttribute, false /*default value*/); if (generateUpdateViews && distinctFlag) { AddToSchemaErrors(Strings.Mapping_DistinctFlagInReadWriteContainer, StorageMappingErrorCode.DistinctFragmentInReadWriteContainer, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } tableName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.MappingFragmentStoreEntitySetAttribute); StorageMappingFragment fragment = LoadMappingFragment(nav.Clone(), entityTypeMapping, tableName, storageEntityContainerType, distinctFlag); //The fragment can be null in the cases of validation errors. if (fragment != null) { entityTypeMapping.AddFragment(fragment); } } } while (nav.MoveToNext(XPathNodeType.Element)); } else { if (nav.LocalName == StorageMslConstructs.ModificationFunctionMappingElement) { // function mappings cannot exist in the context of a table mapping AddToSchemaErrors(Strings.Mapping_Invalid_Function_Mapping_In_Table_Context_0, StorageMappingErrorCode.InvalidTableNameAttributeWithFunctionMapping, m_sourceLocation, xmlLineInfoNav , m_parsingErrors); } if (generateUpdateViews && distinctFlagAboveType) { AddToSchemaErrors(Strings.Mapping_DistinctFlagInReadWriteContainer, StorageMappingErrorCode.DistinctFragmentInReadWriteContainer, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } StorageMappingFragment fragment = LoadMappingFragment(nav.Clone(), entityTypeMapping, tableName, storageEntityContainerType, distinctFlagAboveType); //The fragment can be null in the cases of validation errors. if (fragment != null) { entityTypeMapping.AddFragment(fragment); } } entitySetMapping.AddTypeMapping(entityTypeMapping); } /// /// Loads function mappings for entity type. /// /// /// /// private void LoadEntityTypeFunctionMapping(XPathNavigator nav, StorageEntitySetMapping entitySetMapping, StorageEntityTypeMapping entityTypeMapping) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; // Function mappings can apply only to a single type. if (entityTypeMapping.IsOfTypes.Count != 0 || entityTypeMapping.Types.Count != 1) { AddToSchemaErrors(Strings.Mapping_Invalid_Function_Mapping_Multiple_Types_0, StorageMappingErrorCode.InvalidFunctionMappingForMultipleTypes, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return; } EntityType entityType = (EntityType)entityTypeMapping.Types[0]; //Function Mapping is not allowed to be defined for Abstract Types if (entityType.Abstract) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_AbstractEntity_FunctionMapping_1, entityType.FullName, StorageMappingErrorCode.MappingForAbstractEntityType, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return; } // check that no mapping exists for this entity type already foreach (StorageEntityTypeFunctionMapping existingFunctionMapping in entitySetMapping.FunctionMappings) { if (existingFunctionMapping.EntityType.Equals(entityType)) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_Invalid_Function_Mapping_RedundantEntityTypeMapping_1, entityType.Name, StorageMappingErrorCode.RedundantEntityTypeMappingInFunctionMapping, m_sourceLocation, xmlLineInfoNav , m_parsingErrors); return; } } // create function loader FunctionMappingLoader functionLoader = new FunctionMappingLoader(this, entitySetMapping.Set); // Load all function definitions (for insert, delete and update) StorageFunctionMapping deleteFunctionMapping = null; StorageFunctionMapping insertFunctionMapping = null; StorageFunctionMapping updateFunctionMapping = null; if (nav.MoveToChild(XPathNodeType.Element)) { do { switch (nav.LocalName) { case StorageMslConstructs.DeleteFunctionElement: deleteFunctionMapping = functionLoader.LoadEntityTypeFunctionMapping(nav.Clone(), entitySetMapping.Set, false, true, entityType); break; case StorageMslConstructs.InsertFunctionElement: insertFunctionMapping = functionLoader.LoadEntityTypeFunctionMapping(nav.Clone(), entitySetMapping.Set, true, false, entityType); break; case StorageMslConstructs.UpdateFunctionElement: updateFunctionMapping = functionLoader.LoadEntityTypeFunctionMapping(nav.Clone(), entitySetMapping.Set, true, true, entityType); break; } } while (nav.MoveToNext(XPathNodeType.Element)); } // Ensure that assocation set end mappings bind to the same end (e.g., in Person Manages Person // self-association, ensure that the manager end or the report end is mapped but not both) IEnumerableparameterList = new List (); if (null != deleteFunctionMapping) { parameterList = Helper.Concat(parameterList, deleteFunctionMapping.ParameterBindings); } if (null != insertFunctionMapping) { parameterList = Helper.Concat(parameterList, insertFunctionMapping.ParameterBindings); } if (null != updateFunctionMapping) { parameterList = Helper.Concat(parameterList, updateFunctionMapping.ParameterBindings); } var associationEnds = new Dictionary (); foreach (StorageFunctionParameterBinding parameterBinding in parameterList) { if (null != parameterBinding.MemberPath.AssociationSetEnd) { AssociationSet associationSet = parameterBinding.MemberPath.AssociationSetEnd.ParentAssociationSet; // the "end" corresponds to the second member in the path, e.g. // ID<-Manager where Manager is the end AssociationEndMember currentEnd = parameterBinding.MemberPath.AssociationSetEnd.CorrespondingAssociationEndMember; AssociationEndMember existingEnd; if (associationEnds.TryGetValue(associationSet, out existingEnd) && existingEnd != currentEnd) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_Invalid_Function_Mapping_MultipleEndsOfAssociationMapped_3( currentEnd.Name, existingEnd.Name, associationSet.Name), StorageMappingErrorCode.InvalidFunctionMappingMultipleEndsOfAssociationMapped, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return; } else { associationEnds[associationSet] = currentEnd; } } } // Register the function mapping on the entity set mapping StorageEntityTypeFunctionMapping functionMapping = new StorageEntityTypeFunctionMapping( entityType, deleteFunctionMapping, insertFunctionMapping, updateFunctionMapping); entitySetMapping.AddFunctionMapping(functionMapping); } /// /// The method loads the query view for the Set Mapping node /// into the internal datastructures. /// private bool LoadQueryView(XPathNavigator nav, StorageSetMapping setMapping) { Debug.Assert(nav.LocalName == StorageMslConstructs.QueryViewElement); string queryView = nav.Value; bool includeSubtypes = false; string typeNameString = StorageMappingItemLoader.GetAttributeValue(nav.Clone(), StorageMslConstructs.EntitySetMappingTypeNameAttribute); if (typeNameString != null) { typeNameString = typeNameString.Trim(); } if (setMapping.QueryView == null) { //QV must be the special-case first view if (typeNameString != null) { AddToSchemaErrorsWithMemberInfo(val => Strings.Mapping_TypeName_For_First_QueryView, setMapping.Set.Name, StorageMappingErrorCode.TypeNameForFirstQueryView, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); return false; } if (String.IsNullOrEmpty(queryView)) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_Empty_QueryView_1, setMapping.Set.Name, StorageMappingErrorCode.EmptyQueryView, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); return false; } setMapping.QueryView = queryView; this.m_hasQueryViews = true; return true; } else { //QV must be typeof or typeofonly view if (typeNameString == null || typeNameString.Trim().Length == 0) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_QueryView_TypeName_Not_Defined, setMapping.Set.Name, StorageMappingErrorCode.NoTypeNameForTypeSpecificQueryView, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); return false; } //Get entity types SetentityTypes; Set isOfTypeEntityTypes; EntityType rootEntityType = (EntityType)setMapping.Set.ElementType; if (!TryParseEntityTypeAttribute(nav.Clone(), rootEntityType, e => System.Data.Entity.Strings.Mapping_InvalidContent_Entity_Type_For_Entity_Set_3(e.FullName, rootEntityType.FullName, setMapping.Set.Name), out isOfTypeEntityTypes, out entityTypes)) { // Return if we cannot parse entity types return false; } Debug.Assert(isOfTypeEntityTypes.Count > 0 || entityTypes.Count > 0); Debug.Assert(!(isOfTypeEntityTypes.Count > 0 && entityTypes.Count > 0)); EntityType entityType; if (isOfTypeEntityTypes.Count == 1) { //OfType View entityType = isOfTypeEntityTypes.First(); includeSubtypes = true; } else if (entityTypes.Count == 1) { //OfTypeOnly View entityType = entityTypes.First(); includeSubtypes = false; } else { //More than one type AddToSchemaErrorsWithMemberInfo(Strings.Mapping_QueryViewMultipleTypeInTypeName, setMapping.Set.ToString(), StorageMappingErrorCode.TypeNameContainsMultipleTypesForQueryView, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); return false; } //Check if IsTypeOf(A) and A is the base type if (includeSubtypes && setMapping.Set.ElementType.EdmEquals(entityType)) { //Don't allow TypeOFOnly(a) if a is a base type. AddToSchemaErrorWithMemberAndStructure(Strings.Mapping_QueryView_For_Base_Type, entityType.ToString(), setMapping.Set.ToString(), StorageMappingErrorCode.IsTypeOfQueryViewForBaseType, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); return false; } if (String.IsNullOrEmpty(queryView)) { if (includeSubtypes) { AddToSchemaErrorWithMemberAndStructure(Strings.Mapping_Empty_QueryView_OfType_2, entityType.Name, setMapping.Set.Name, StorageMappingErrorCode.EmptyQueryView, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); return false; } else { AddToSchemaErrorWithMemberAndStructure(Strings.Mapping_Empty_QueryView_OfTypeOnly_2, setMapping.Set.Name, entityType.Name, StorageMappingErrorCode.EmptyQueryView, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); return false; } } //Add it to the QV cache Triple key = new Triple(setMapping.Set, new Pair (entityType, includeSubtypes)); if (setMapping.ContainsTypeSpecificQueryView(key)) { //two QVs for the same type EdmSchemaError error = null; if (includeSubtypes) { error = new EdmSchemaError( Strings.Mapping_QueryView_Duplicate_OfType(setMapping.Set, entityType), (int)StorageMappingErrorCode.QueryViewExistsForEntitySetAndType, EdmSchemaErrorSeverity.Error, m_sourceLocation, ((IXmlLineInfo)nav).LineNumber, ((IXmlLineInfo)nav).LinePosition); } else { error = new EdmSchemaError( Strings.Mapping_QueryView_Duplicate_OfTypeOnly(setMapping.Set, entityType), (int)StorageMappingErrorCode.QueryViewExistsForEntitySetAndType, EdmSchemaErrorSeverity.Error, m_sourceLocation, ((IXmlLineInfo)nav).LineNumber, ((IXmlLineInfo)nav).LinePosition); } m_parsingErrors.Add(error); return false; } setMapping.AddTypeSpecificQueryView(key, queryView); return true; } } /// /// The method loads the child nodes for the AssociationSet Mapping node /// into the internal datastructures. /// /// /// /// private void LoadAssociationSetMapping(XPathNavigator nav, StorageEntityContainerMapping entityContainerMapping, EntityContainer storageEntityContainerType) { IXmlLineInfo navLineInfo = (IXmlLineInfo)nav; //Get the AssociationSet name string associationSetName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.AssociationSetMappingNameAttribute); //Get the AssociationType name, need to parse it if the mapping information is being specified for multiple types string associationTypeName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.AssociationSetMappingTypeNameAttribute); //Get the table name. This might be emptystring since the user can have a TableMappingFragment instead of this. string tableName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.EntitySetMappingStoreEntitySetAttribute); //Try to find the AssociationSet with the given name in the EntityContainer. RelationshipSet relationshipSet; entityContainerMapping.EdmEntityContainer.TryGetRelationshipSetByName(associationSetName, false /*ignoreCase*/, out relationshipSet); AssociationSet associationSet = relationshipSet as AssociationSet; //If no AssociationSet with the given name exists, than Add a schema error and return if (associationSet == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Association_Set_1, associationSetName, StorageMappingErrorCode.InvalidAssociationSet, m_sourceLocation, navLineInfo, m_parsingErrors); //There is no point in continuing the loading of association set map if the AssociationSetName has a problem return; } if (associationSet.ElementType.IsForeignKey) { EdmSchemaError error = AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_ForeignKey_Association_Set_1, associationSetName, StorageMappingErrorCode.InvalidAssociationSet, m_sourceLocation, navLineInfo, m_parsingErrors); ReferentialConstraint constraint = associationSet.ElementType.ReferentialConstraints.Single(); IEnumerabledependentKeys = MetadataHelper.GetEntityTypeForEnd((AssociationEndMember)constraint.ToRole).KeyMembers; if (associationSet.ElementType.ReferentialConstraints.Single().ToProperties.All(p => dependentKeys.Contains(p))) { //Downgrade to a warning if the foreign key constraint is between keys (for back-compat reasons) error.Severity = EdmSchemaErrorSeverity.Warning; } return; } if (entityContainerMapping.ContainsAssociationSetMapping(associationSet)) { //Can not add this set mapping since our storage dictionary won't allow //duplicate maps AddToSchemaErrorsWithMemberInfo(Strings.Mapping_Duplicate_CdmAssociationSet_StorageMap_1, associationSetName, StorageMappingErrorCode.DuplicateSetMapping, m_sourceLocation, navLineInfo, m_parsingErrors); return; } //Create the AssociationSet Mapping which contains the mapping information for association set. StorageAssociationSetMapping setMapping = new StorageAssociationSetMapping(associationSet, entityContainerMapping); //Set the Start Line Information on Fragment setMapping.StartLineNumber = navLineInfo.LineNumber; setMapping.StartLinePosition = navLineInfo.LinePosition; if (!nav.MoveToChild(XPathNodeType.Element)) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Emtpty_SetMap_1, associationSet.Name, StorageMappingErrorCode.EmptySetMapping, m_sourceLocation, navLineInfo, m_parsingErrors); return; } entityContainerMapping.AddAssociationSetMapping(setMapping); //If there is a query view it has to be the first element if (nav.LocalName == StorageMslConstructs.QueryViewElement) { if (!(String.IsNullOrEmpty(tableName))) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_TableName_QueryView_1, associationSetName, StorageMappingErrorCode.TableNameAttributeWithQueryView, m_sourceLocation, navLineInfo, m_parsingErrors); return; } //Load the Query View into the set mapping, //if you get an error, return immediately since //you go on, you could be giving lot of dubious errors if (!LoadQueryView(nav.Clone(), setMapping)) { return; } //If there are no more elements just return if (!nav.MoveToNext(XPathNodeType.Element)) { return; } } if ((nav.LocalName == StorageMslConstructs.EndPropertyMappingElement) || (nav.LocalName == StorageMslConstructs.ModificationFunctionMappingElement)) { if ((String.IsNullOrEmpty(associationTypeName))) { AddToSchemaErrors(Strings.Mapping_InvalidContent_Association_Type_Empty_0, StorageMappingErrorCode.InvalidAssociationType, m_sourceLocation, navLineInfo, m_parsingErrors); return; } //Load the AssociationTypeMapping into memory. LoadAssociationTypeMapping(nav.Clone(), setMapping, associationTypeName, tableName, storageEntityContainerType); } else if (nav.LocalName == StorageMslConstructs.ConditionElement) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_AssociationSet_Condition_1, associationSetName, StorageMappingErrorCode.InvalidContent, m_sourceLocation, navLineInfo, m_parsingErrors); return; } else { Debug.Assert(false, "XSD validation should ensure this"); } } /// /// The method loads a function import mapping element /// /// /// /// private void LoadFunctionImportMapping(XPathNavigator nav, StorageEntityContainerMapping entityContainerMapping, EntityContainer storageEntityContainerType) { IXmlLineInfo lineInfo = (IXmlLineInfo)(nav.Clone()); // Get target (store) function EdmFunction targetFunction; if (!TryGetFunctionImportStoreFunction(nav, out targetFunction)) { return; } // Get source (model) function EdmFunction functionImport; if (!TryGetFunctionImportModelFunction(nav, entityContainerMapping, out functionImport)) { return; } // Validate parameters are compatible between the store and model functions ValidateFunctionImportMappingParameters(nav, targetFunction, functionImport); // Process type mapping information ListtypeMappings = new List (); if (nav.MoveToChild(XPathNodeType.Element)) { if (nav.LocalName == StorageMslConstructs.FunctionImportMappingResultMapping) { if (nav.MoveToChild(XPathNodeType.Element)) { do { FunctionImportStructuralTypeMapping typeMapping; if (nav.LocalName == StorageMslConstructs.EntityTypeMappingElement) { if (TryLoadFunctionImportEntityTypeMapping(nav.Clone(), targetFunction, functionImport, out typeMapping)) { typeMappings.Add(typeMapping); } } else if (nav.LocalName == StorageMslConstructs.ComplexTypeMappingElement) { if (TryLoadFunctionImportComplexTypeMapping(nav.Clone(), targetFunction, functionImport, out typeMapping)) { typeMappings.Add(typeMapping); } } } while (nav.MoveToNext(XPathNodeType.Element)); } } } // Add import mapping to list FunctionImportMapping mapping = new FunctionImportMapping(targetFunction, functionImport, typeMappings.Cast (), this.EdmItemCollection); entityContainerMapping.AddFunctionImportMapping(functionImport, mapping); // Verify that all types can be produced KeyToListMap unreachableEntityTypes; KeyToListMap unreachableIsTypeOfs; mapping.GetUnreachableTypes(EdmItemCollection, out unreachableEntityTypes, out unreachableIsTypeOfs); foreach (var unreachableEntityType in unreachableEntityTypes.KeyValuePairs) { string lines = StringUtil.ToCommaSeparatedString(unreachableEntityType.Value.Select(li => li.LineNumber)); AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_UnreachableType(unreachableEntityType.Key.FullName, lines), StorageMappingErrorCode.MappingFunctionImportAmbiguousTypeConditions, m_sourceLocation, lineInfo, m_parsingErrors); } foreach (var unreachableIsTypeOf in unreachableIsTypeOfs.KeyValuePairs) { string lines = StringUtil.ToCommaSeparatedString(unreachableIsTypeOf.Value.Select(li => li.LineNumber)); string isTypeOfDescription = StorageMslConstructs.IsTypeOf + unreachableIsTypeOf.Key.FullName + StorageMslConstructs.IsTypeOfTerminal; AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_UnreachableIsTypeOf(isTypeOfDescription, lines), StorageMappingErrorCode.MappingFunctionImportAmbiguousTypeConditions, m_sourceLocation, lineInfo, m_parsingErrors); } // Verify that function imports returning abstract types include explicit mappings EntityType returnEntityType; if (MetadataHelper.TryGetFunctionImportReturnType (functionImport, out returnEntityType) && returnEntityType.Abstract && mapping.NormalizedEntityTypeMappings.Count == 0) { AddToSchemaErrorWithMemberAndStructure(Strings.Mapping_InvalidContent_ImplicitMappingForAbstractReturnType_FunctionMapping_1, returnEntityType.FullName, functionImport.FullName, StorageMappingErrorCode.MappingForAbstractEntityType, m_sourceLocation, lineInfo, m_parsingErrors); } } private bool TryGetFunctionImportStoreFunction(XPathNavigator nav, out EdmFunction targetFunction) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; targetFunction = null; // Get the function name string functionName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.FunctionImportMappingFunctionNameAttribute); // Try to find the function definition ReadOnlyCollection functionOverloads = this.StoreItemCollection.GetFunctions(functionName); if (functionOverloads.Count == 0) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_StoreFunctionDoesNotExist(functionName), StorageMappingErrorCode.MappingFunctionImportStoreFunctionDoesNotExist, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return false; } else if (1 < functionOverloads.Count) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_StoreFunctionAmbiguous(functionName), StorageMappingErrorCode.MappingFunctionImportStoreFunctionAmbiguous, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return false; } targetFunction = functionOverloads.Single(); // validate target function is supported (non-composable, etc.) if (targetFunction.IsComposableAttribute) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_TargetFunctionMustBeComposable(targetFunction.FullName), StorageMappingErrorCode.MappingFunctionImportTargetFunctionMustBeComposable, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return false; } return true; } private bool TryGetFunctionImportModelFunction(XPathNavigator nav, StorageEntityContainerMapping entityContainerMapping, out EdmFunction functionImport) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; // Get the function import name string functionImportName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.FunctionImportMappingFunctionImportNameAttribute); // Try to find the function import EntityContainer modelContainer = entityContainerMapping.EdmEntityContainer; functionImport = null; foreach (EdmFunction functionImportCandidate in modelContainer.FunctionImports) { if (functionImportCandidate.Name == functionImportName) { functionImport = functionImportCandidate; break; } } if (null == functionImport) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_FunctionImportDoesNotExist(functionImportName, entityContainerMapping.EdmEntityContainer.Name), StorageMappingErrorCode.MappingFunctionImportFunctionImportDoesNotExist, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return false; } // check that no existing mapping exists for this function import FunctionImportMapping targetFunctionCollision; if (entityContainerMapping.TryGetFunctionImportMapping(functionImport, out targetFunctionCollision)) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_FunctionImportMappedMultipleTimes(functionImportName), StorageMappingErrorCode.MappingFunctionImportFunctionImportMappedMultipleTimes, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return false; } return true; } private void ValidateFunctionImportMappingParameters(XPathNavigator nav, EdmFunction targetFunction, EdmFunction functionImport) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; foreach (FunctionParameter targetParameter in targetFunction.Parameters) { // find corresponding import parameter FunctionParameter importParameter; if (!functionImport.Parameters.TryGetValue(targetParameter.Name, false, out importParameter)) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_TargetParameterHasNoCorrespondingImportParameter(targetParameter.Name), StorageMappingErrorCode.MappingFunctionImportTargetParameterHasNoCorrespondingImportParameter, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } else { // parameters must have the same direction (in|out) if (targetParameter.Mode != importParameter.Mode) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_IncompatibleParameterMode(targetParameter.Name, targetParameter.Mode, importParameter.Mode), StorageMappingErrorCode.MappingFunctionImportIncompatibleParameterMode, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } // there are no type facets declared for function parameter types; // we simply verify the primitive type kind is equivalent PrimitiveType importType = (PrimitiveType)importParameter.TypeUsage.EdmType; PrimitiveType cspaceTargetType = (PrimitiveType)StoreItemCollection.StoreProviderManifest.GetEdmType(targetParameter.TypeUsage).EdmType; if (cspaceTargetType == null) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_ProviderReturnsNullType(targetParameter.Name), StorageMappingErrorCode.MappingStoreProviderReturnsNullEdmType, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); return; } if (cspaceTargetType.PrimitiveTypeKind != importType.PrimitiveTypeKind) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_IncompatibleParameterType(targetParameter.Name, cspaceTargetType.Name, importType.Name), StorageMappingErrorCode.MappingFunctionImportIncompatibleParameterType, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } } } foreach (FunctionParameter importParameter in functionImport.Parameters) { // find corresponding target parameter FunctionParameter targetParameter; if (!targetFunction.Parameters.TryGetValue(importParameter.Name, false, out targetParameter)) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_FunctionImport_ImportParameterHasNoCorrespondingTargetParameter(importParameter.Name), StorageMappingErrorCode.MappingFunctionImportImportParameterHasNoCorrespondingTargetParameter, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } } } private bool TryLoadFunctionImportComplexTypeMapping(XPathNavigator nav, EdmFunction targetFunction, EdmFunction functionImport, out FunctionImportStructuralTypeMapping typeMapping) { typeMapping = null; IXmlLineInfo lineInfo = (IXmlLineInfo)(nav.Clone()); ComplexType complexType; if (!TryParseComplexTypeAttribute(nav, out complexType)) { // can not find complex type AddToSchemaErrorWithMessage( Strings.Mapping_FunctionImport_InvalidComplexTypeName( GetAttributeValue(nav.Clone(), StorageMslConstructs.ComplexTypeMappingTypeNameAttribute), functionImport.Name), StorageMappingErrorCode.MappingFunctionImportInvalidComplexTypeName, m_sourceLocation, lineInfo, m_parsingErrors); return false; } else { Collection columnRenameMappings = new Collection (); if (!LoadFunctionImportStructuralType(nav.Clone(), new List () { complexType }, complexType.Name, functionImport.Name, columnRenameMappings, null)) { return false; } typeMapping = new FunctionImportComplexTypeMapping(lineInfo, complexType, columnRenameMappings); return true; } } private bool TryParseComplexTypeAttribute(XPathNavigator nav, out ComplexType complexType) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; string complexTypeAttribute = GetAttributeValue(nav.Clone(), StorageMslConstructs.ComplexTypeMappingTypeNameAttribute); complexTypeAttribute = GetAliasResolvedValue(complexTypeAttribute); if (this.EdmItemCollection.TryGetItem (complexTypeAttribute, out complexType)) { return true; } return false; } private bool TryLoadFunctionImportEntityTypeMapping(XPathNavigator nav, EdmFunction targetFunction, EdmFunction functionImport, out FunctionImportStructuralTypeMapping typeMapping) { typeMapping = null; IXmlLineInfo lineInfo = (IXmlLineInfo)(nav.Clone()); // cannot specify an entity type mapping for a function import that does not return members // of an entity set if (null == functionImport.EntitySet) { AddToSchemaErrors(System.Data.Entity.Strings.Mapping_FunctionImport_EntityTypeMappingForFunctionNotReturningEntitySet( StorageMslConstructs.EntityTypeMappingElement, functionImport.FullName), StorageMappingErrorCode.MappingFunctionImportEntityTypeMappingForFunctionNotReturningEntitySet, m_sourceLocation, lineInfo, m_parsingErrors); } // process entity type string entityTypeString = GetAttributeValue(nav.Clone(), StorageMslConstructs.EntitySetMappingTypeNameAttribute); Set isOfTypeEntityTypes; Set entityTypes; { // verify the entity type is appropriate to the function import's entity type EntityType rootEntityType; if (!MetadataHelper.TryGetFunctionImportReturnType (functionImport, out rootEntityType) || !TryParseEntityTypeAttribute(nav.Clone(), rootEntityType, e => System.Data.Entity.Strings.Mapping_FunctionImport_InvalidContentEntityTypeForEntitySet(e.FullName, rootEntityType.FullName, functionImport.EntitySet.Name, functionImport.FullName), out isOfTypeEntityTypes, out entityTypes)) { return false; } } IEnumerable currentTypesInHierachy = isOfTypeEntityTypes.Concat(entityTypes).Distinct().OfType (); Collection columnRenameMappings = new Collection (); // process all conditions and column renames List conditions = new List (); if (!LoadFunctionImportStructuralType(nav.Clone(), currentTypesInHierachy, entityTypeString, functionImport.Name, columnRenameMappings, conditions)) { return false; } typeMapping = new FunctionImportEntityTypeMapping(isOfTypeEntityTypes, entityTypes, conditions, lineInfo, columnRenameMappings); return true; } private bool LoadFunctionImportStructuralType( XPathNavigator nav, IEnumerable currentTypes, string typeName, string functionImportName, Collection columnRenameMappings, List conditions) { Debug.Assert(null != columnRenameMappings, "columnRenameMappings cannot be null"); Debug.Assert(null != nav, "nav cannot be null"); Debug.Assert(null != currentTypes, "currentTypes cannot be null"); IXmlLineInfo lineInfo = (IXmlLineInfo)(nav.Clone()); if (nav.MoveToChild(XPathNodeType.Element)) { do { if (nav.LocalName == StorageMslConstructs.ScalarPropertyElement) { LoadFunctionImportStructuralTypeMappingScalarProperty(nav, columnRenameMappings, currentTypes, typeName, functionImportName); } if (nav.LocalName == StorageMslConstructs.ConditionElement) { LoadFunctionImportEntityTypeMappingCondition(nav, conditions); } } while (nav.MoveToNext(XPathNodeType.Element)); } if (null != conditions) { // make sure a single condition is specified per column HashSet columnsWithConditions = new HashSet (); foreach (var condition in conditions) { if (!columnsWithConditions.Add(condition.ColumnName)) { AddToSchemaErrorWithMessage( System.Data.Entity.Strings.Mapping_FunctionImport_MultipleConditionsOnSingleColumn(condition.ColumnName), StorageMappingErrorCode.MappingFunctionMultipleTypeConditionsForOneColumn, m_sourceLocation, lineInfo, m_parsingErrors); return false; } } } return true; } private void LoadFunctionImportStructuralTypeMappingScalarProperty( XPathNavigator nav, Collection columnRenameMappings, IEnumerable currentTypes, string returnType, string functionImportName) { IXmlLineInfo lineInfo = (IXmlLineInfo)(nav.Clone()); string memberName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ScalarPropertyNameAttribute); string columnName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ScalarPropertyColumnNameAttribute); // Negative case: the property name is invalid if (!currentTypes.All(t=>t.Members.Contains(memberName))) { AddToSchemaErrorWithMessage( Strings.Mapping_FunctionImport_InvalidMemberName(memberName, returnType, functionImportName), StorageMappingErrorCode.MappingFunctionImportInvalidMemberName, m_sourceLocation, lineInfo, m_parsingErrors); } if (columnRenameMappings.Any(m => m.CMember == memberName)) { // Negative case: duplicate member name mapping in one type rename mapping AddToSchemaErrorWithMessage( Strings.Mapping_FunctionImport_DuplicateMemberName(memberName, returnType, functionImportName), StorageMappingErrorCode.MappingFunctionImportDuplicateMemberName, m_sourceLocation, lineInfo, m_parsingErrors); } else { columnRenameMappings.Add(new FunctionImportReturnTypeScalarPropertyMapping(memberName, columnName)); } } private void LoadFunctionImportEntityTypeMappingCondition( XPathNavigator nav, List conditions) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; string columnName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ConditionColumnNameAttribute); string value = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ConditionValueAttribute); string isNull = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ConditionIsNullAttribute); //Either Value or NotNull need to be specifid on the condition mapping but not both if ((isNull != null) && (value != null)) { AddToSchemaErrors(Strings.Mapping_InvalidContent_ConditionMapping_Both_Values_0, StorageMappingErrorCode.ConditionError, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } else if ((isNull == null) && (value == null)) { AddToSchemaErrors(Strings.Mapping_InvalidContent_ConditionMapping_Either_Values_0, StorageMappingErrorCode.ConditionError, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } else { if (isNull != null) { bool isNullValue = Convert.ToBoolean(isNull, CultureInfo.InvariantCulture); conditions.Add(new FunctionImportEntityTypeMappingConditionIsNull(columnName, isNullValue)); } else { XPathNavigator columnValue = nav.Clone(); columnValue.MoveToAttribute(StorageMslConstructs.ConditionValueAttribute, string.Empty); conditions.Add(new FunctionImportEntityTypeMappingConditionValue(columnName, columnValue)); } } } /// /// The method loads the child nodes for the AssociationType Mapping node /// into the internal datastructures. /// /// /// /// /// /// private void LoadAssociationTypeMapping(XPathNavigator nav, StorageAssociationSetMapping associationSetMapping, string associationTypeName, string tableName, EntityContainer storageEntityContainerType) { IXmlLineInfo navLineInfo = (IXmlLineInfo)nav; //Get the association type for association type name specified in MSL //If no AssociationType with the given name exists, add a schema error and return AssociationType associationType; this.EdmItemCollection.TryGetItem(associationTypeName, out associationType); if (associationType == null) { //There is no point in continuing loading if the AssociationType is null AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Association_Type_1, associationTypeName, StorageMappingErrorCode.InvalidAssociationType, m_sourceLocation, navLineInfo, m_parsingErrors); return; } //Verify that AssociationType specified should be the declared type of //AssociationSet or a derived Type of it. //Future Enhancement : Change the code to use EdmEquals if ((!(associationSetMapping.Set.ElementType.Equals(associationType)))) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_Invalid_Association_Type_For_Association_Set_3(associationTypeName, associationSetMapping.Set.ElementType.FullName, associationSetMapping.Set.Name), StorageMappingErrorCode.DuplicateTypeMapping, m_sourceLocation, navLineInfo, m_parsingErrors); return; } //Create an AssociationTypeMapping to hold the information for AssociationType mapping. StorageAssociationTypeMapping associationTypeMapping = new StorageAssociationTypeMapping(associationType, associationSetMapping); associationSetMapping.AddTypeMapping(associationTypeMapping); //If the table name was not specified on the AssociationSetMapping element //Then there should have been a query view. Otherwise throw. if (String.IsNullOrEmpty(tableName) && (associationSetMapping.QueryView == null)) { AddToSchemaErrors(Strings.Mapping_InvalidContent_Table_Expected_0, StorageMappingErrorCode.InvalidTable, m_sourceLocation, navLineInfo, m_parsingErrors); } else { StorageMappingFragment fragment = LoadAssociationMappingFragment(nav.Clone(), associationSetMapping, associationTypeMapping, tableName, storageEntityContainerType); if (fragment != null) { //Fragment can be null because of validation errors associationTypeMapping.AddFragment(fragment); } } } /// /// Loads function mappings for the entity type. /// /// /// /// private void LoadAssociationTypeFunctionMapping(XPathNavigator nav, StorageAssociationSetMapping associationSetMapping, StorageAssociationTypeMapping associationTypeMapping) { // create function loader FunctionMappingLoader functionLoader = new FunctionMappingLoader(this, associationSetMapping.Set); // Load all function definitions (for insert, delete and update) StorageFunctionMapping deleteFunctionMapping = null; StorageFunctionMapping insertFunctionMapping = null; if (nav.MoveToChild(XPathNodeType.Element)) { do { switch (nav.LocalName) { case StorageMslConstructs.DeleteFunctionElement: deleteFunctionMapping = functionLoader.LoadAssociationSetFunctionMapping(nav.Clone(), associationSetMapping.Set, false); break; case StorageMslConstructs.InsertFunctionElement: insertFunctionMapping = functionLoader.LoadAssociationSetFunctionMapping(nav.Clone(), associationSetMapping.Set, true); break; } } while (nav.MoveToNext(XPathNodeType.Element)); } // register function mapping information associationSetMapping.FunctionMapping = new StorageAssociationSetFunctionMapping( (AssociationSet)associationSetMapping.Set, deleteFunctionMapping, insertFunctionMapping); } ////// The method loads the child nodes for the TableMappingFragment under the EntityType node /// into the internal datastructures. /// /// /// /// /// ///private StorageMappingFragment LoadMappingFragment(XPathNavigator nav, StorageEntityTypeMapping typeMapping, string tableName , EntityContainer storageEntityContainerType, bool distinctFlag) { IXmlLineInfo navLineInfo = (IXmlLineInfo)nav; //First make sure that there was no QueryView specified for this Set if (typeMapping.SetMapping.QueryView != null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_QueryView_PropertyMaps_1, typeMapping.SetMapping.Set.Name, StorageMappingErrorCode.PropertyMapsWithQueryView, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } //Get the table type that represents this table EntitySet tableMember; storageEntityContainerType.TryGetEntitySetByName(tableName, false /*ignoreCase*/, out tableMember); if (tableMember == null) { //There is no point in continuing loading if the Table on S side can not be found AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Table_1, tableName, StorageMappingErrorCode.InvalidTable, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } EntityType tableType = tableMember.ElementType; //Create a table mapping fragment to hold the mapping information for a TableMappingFragment node StorageMappingFragment fragment = new StorageMappingFragment(tableMember, typeMapping, distinctFlag); //Set the Start Line Information on Fragment fragment.StartLineNumber = navLineInfo.LineNumber; fragment.StartLinePosition = navLineInfo.LinePosition; //Go through the property mappings for this TableMappingFragment and load them in memory. if (nav.MoveToChild(XPathNodeType.Element)) { do { //need to get the type that this member exists in EdmType containerType = null; string propertyName = StorageMappingItemLoader.GetAttributeValue(nav.Clone(), StorageMslConstructs.ComplexPropertyNameAttribute); //PropertyName could be null for Condition Maps if (propertyName != null) { containerType = typeMapping.GetContainerType(propertyName); } switch (nav.LocalName) { case StorageMslConstructs.ScalarPropertyElement: StorageScalarPropertyMapping scalarMap = LoadScalarPropertyMapping(nav.Clone(), containerType, tableType); if (scalarMap != null) { //scalarMap can be null in invalid cases fragment.AddProperty(scalarMap); } break; case StorageMslConstructs.ComplexPropertyElement: StorageComplexPropertyMapping complexMap = LoadComplexPropertyMapping(nav.Clone(), containerType, tableType); //Complex Map can be null in case of invalid MSL files. if (complexMap != null) { fragment.AddProperty(complexMap); } break; case StorageMslConstructs.ConditionElement: StorageConditionPropertyMapping conditionMap = LoadConditionPropertyMapping(nav.Clone(), containerType, tableType); //conditionMap can be null in cases of invalid Map if (conditionMap != null) { if (!fragment.AddConditionProperty(conditionMap)) { EdmProperty conditionMember = (conditionMap.EdmProperty != null) ? conditionMap.EdmProperty : conditionMap.ColumnProperty; AddToSchemaErrorsWithMemberInfo( Strings.Mapping_InvalidContent_Duplicate_Condition_Member_1, conditionMember.Name, StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } } break; default: AddToSchemaErrors(Strings.Mapping_InvalidContent_General_0, StorageMappingErrorCode.InvalidContent, m_sourceLocation, navLineInfo, m_parsingErrors); break; } } while (nav.MoveToNext(XPathNodeType.Element)); } //Set the end Line Information on Fragment fragment.EndLineNumber = navLineInfo.LineNumber; fragment.EndLinePosition = navLineInfo.LinePosition; nav.MoveToChild(XPathNodeType.Element); return fragment; } /// /// The method loads the child nodes for the TableMappingFragment under the AssociationType node /// into the internal datastructures. /// /// /// /// /// /// ///private StorageMappingFragment LoadAssociationMappingFragment(XPathNavigator nav, StorageAssociationSetMapping setMapping, StorageAssociationTypeMapping typeMapping, string tableName, EntityContainer storageEntityContainerType) { IXmlLineInfo navLineInfo = (IXmlLineInfo)nav; StorageMappingFragment fragment = null; EntityType tableType = null; //If there is a query view, Dont create a mapping fragment since there should n't be one if (setMapping.QueryView == null) { //Get the table type that represents this table EntitySet tableMember; storageEntityContainerType.TryGetEntitySetByName(tableName, false /*ignoreCase*/, out tableMember); if (tableMember == null) { //There is no point in continuing loading if the Table is null AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Table_1, tableName, StorageMappingErrorCode.InvalidTable, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } tableType = tableMember.ElementType; //Create a Mapping fragment and load all the End node under it fragment = new StorageMappingFragment(tableMember, typeMapping, false /*No distinct flag*/); //Set the Start Line Information on Fragment, For AssociationSet there are //no fragments, so the start Line Info is same as that of Set fragment.StartLineNumber = setMapping.StartLineNumber; fragment.StartLinePosition = setMapping.StartLinePosition; } do { //need to get the type that this member exists in switch (nav.LocalName) { case StorageMslConstructs.EndPropertyMappingElement: //Make sure that there was no QueryView specified for this Set if (setMapping.QueryView != null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_QueryView_PropertyMaps_1, setMapping.Set.Name, StorageMappingErrorCode.PropertyMapsWithQueryView, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } string endName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.EndPropertyMappingNameAttribute); EdmMember endMember = null; typeMapping.AssociationType.Members.TryGetValue(endName, false, out endMember); AssociationEndMember end = endMember as AssociationEndMember; if (end == null) { //Don't try to load the end property map if the end property itself is null AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_End_1, endName, StorageMappingErrorCode.InvalidEdmMember, m_sourceLocation, navLineInfo, m_parsingErrors); continue; } fragment.AddProperty((LoadEndPropertyMapping(nav.Clone(), end, tableType))); break; case StorageMslConstructs.ConditionElement: //Make sure that there was no QueryView specified for this Set if (setMapping.QueryView != null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_QueryView_PropertyMaps_1, setMapping.Set.Name, StorageMappingErrorCode.PropertyMapsWithQueryView, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } //Need to add validation for conditions in Association mapping fragment. StorageConditionPropertyMapping conditionMap = LoadConditionPropertyMapping(nav.Clone(), null /*containerType*/, tableType); //conditionMap can be null in cases of invalid Map if (conditionMap != null) { if (!fragment.AddConditionProperty(conditionMap)) { EdmProperty conditionMember = (conditionMap.EdmProperty != null) ? conditionMap.EdmProperty : conditionMap.ColumnProperty; AddToSchemaErrorsWithMemberInfo( Strings.Mapping_InvalidContent_Duplicate_Condition_Member_1, conditionMember.Name, StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } } break; case StorageMslConstructs.ModificationFunctionMappingElement: setMapping.HasModificationFunctionMapping = true; LoadAssociationTypeFunctionMapping(nav.Clone(), setMapping, typeMapping); break; default: AddToSchemaErrors(Strings.Mapping_InvalidContent_General_0, StorageMappingErrorCode.InvalidContent, m_sourceLocation, navLineInfo, m_parsingErrors); break; } } while (nav.MoveToNext(XPathNodeType.Element)); if (setMapping.QueryView == null) { Debug.Assert(fragment != null, "There should be Fragment when there is no query view"); //Set the end Line Information on Fragment fragment.EndLineNumber = navLineInfo.LineNumber; fragment.EndLinePosition = navLineInfo.LinePosition; } return fragment; } /// /// The method loads the ScalarProperty mapping /// into the internal datastructures. /// /// /// /// ///private StorageScalarPropertyMapping LoadScalarPropertyMapping(XPathNavigator nav, EdmType containerType, EntityType tableType) { IXmlLineInfo xmlLineInfoNav = (IXmlLineInfo)nav; //Get the property name from MSL. string propertyName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ScalarPropertyNameAttribute); EdmProperty member = null; if (!String.IsNullOrEmpty(propertyName)) { //If the container type is a collection type, there wouldn't be a member to represent this scalar property if (containerType == null || !(Helper.IsCollectionType(containerType))) { //If container type is null that means we have not found the member in any of the IsOfTypes. if (containerType != null) { if (Helper.IsRefType(containerType)) { RefType refType = (RefType)containerType; ((EntityType)refType.ElementType).Properties.TryGetValue(propertyName, false /*ignoreCase*/, out member); } else { EdmMember tempMember; (containerType as StructuralType).Members.TryGetValue(propertyName, false, out tempMember); member = tempMember as EdmProperty; } } if (member == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Cdm_Member_1, propertyName, StorageMappingErrorCode.InvalidEdmMember, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } } } //Get the property from Storeside string columnName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ScalarPropertyColumnNameAttribute); Debug.Assert(columnName != null, "XSD validation should have caught this"); EdmProperty columnMember; tableType.Properties.TryGetValue(columnName, false, out columnMember); if (columnMember == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Column_1, columnName, StorageMappingErrorCode.InvalidStorageMember, m_sourceLocation, xmlLineInfoNav, m_parsingErrors); } //Don't create scalar property map if the property or column metadata is null if ((member == null) || (columnMember == null)) { return null; } if (!Helper.IsPrimitiveType(member.TypeUsage.EdmType)) { EdmSchemaError error = new EdmSchemaError( System.Data.Entity.Strings.Mapping_Invalid_CSide_ScalarProperty_1( member.Name), (int)StorageMappingErrorCode.InvalidTypeInScalarProperty, EdmSchemaErrorSeverity.Error, m_sourceLocation, xmlLineInfoNav.LineNumber, xmlLineInfoNav.LinePosition); m_parsingErrors.Add(error); return null; } ValidateAndUpdateScalarMemberMapping(member, columnMember, xmlLineInfoNav); StorageScalarPropertyMapping scalarPropertyMapping = new StorageScalarPropertyMapping(member, columnMember); return scalarPropertyMapping; } /// /// The method loads the ComplexProperty mapping /// into the internal datastructures. /// /// /// /// ///private StorageComplexPropertyMapping LoadComplexPropertyMapping(XPathNavigator nav, EdmType containerType, EntityType tableType) { IXmlLineInfo navLineInfo = (IXmlLineInfo)nav; CollectionType collectionType = containerType as CollectionType; //Get the property name from MSL string propertyName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ComplexPropertyNameAttribute); //Get the member metadata from the contianer type passed in. //But if the continer type is collection type, there would n't be any member to represent the member. EdmProperty member = null; EdmType memberType = null; //If member specified the type name, it takes precedence string memberTypeName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ComplexTypeMappingTypeNameAttribute); StructuralType containerStructuralType = containerType as StructuralType; if (String.IsNullOrEmpty(memberTypeName)) { if (collectionType == null) { EdmMember tempMember; containerStructuralType.Members.TryGetValue(propertyName, false /*ignoreCase*/, out tempMember); member = tempMember as EdmProperty; if (member == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Cdm_Member_1, propertyName, StorageMappingErrorCode.InvalidEdmMember, m_sourceLocation, navLineInfo, m_parsingErrors); } memberType = member.TypeUsage.EdmType; } else { memberType = collectionType.TypeUsage.EdmType; } } else { //If container type is null that means we have not found the member in any of the IsOfTypes. if (containerType != null) { EdmMember tempMember; containerStructuralType.Members.TryGetValue(propertyName, false /*ignoreCase*/, out tempMember); member = tempMember as EdmProperty; } if (member == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Cdm_Member_1, propertyName, StorageMappingErrorCode.InvalidEdmMember, m_sourceLocation, navLineInfo, m_parsingErrors); } this.EdmItemCollection.TryGetItem (memberTypeName, out memberType); memberType = memberType as ComplexType; // If member type is null, that means the type wasn't found in the workspace if (memberType == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Complex_Type_1, memberTypeName, StorageMappingErrorCode.InvalidComplexType, m_sourceLocation, navLineInfo, m_parsingErrors); } } StorageComplexPropertyMapping complexPropertyMapping = new StorageComplexPropertyMapping(member); XPathNavigator cloneNav = nav.Clone(); bool hasComplexTypeMappingElements = false; if (cloneNav.MoveToChild(XPathNodeType.Element)) { if (cloneNav.LocalName == StorageMslConstructs.ComplexTypeMappingElement) { hasComplexTypeMappingElements = true; } } //There is no point in continuing if the complex member or complex member type is null if ((member == null) || (memberType == null)) { return null; } if (hasComplexTypeMappingElements) { nav.MoveToChild(XPathNodeType.Element); do { complexPropertyMapping.AddTypeMapping(LoadComplexTypeMapping(nav.Clone(), null, tableType)); } while (nav.MoveToNext(XPathNodeType.Element)); } else { complexPropertyMapping.AddTypeMapping(LoadComplexTypeMapping(nav.Clone(), memberType, tableType)); } return complexPropertyMapping; } /// /// /// /// /// private StorageComplexTypeMapping LoadComplexTypeMapping(XPathNavigator nav, EdmType type, EntityType tableType) { //Get the IsPartial attribute from MSL bool isPartial = false; string partialAttribute = StorageMappingItemLoader.GetAttributeValue(nav.Clone(), StorageMslConstructs.ComplexPropertyIsPartialAttribute); if (!String.IsNullOrEmpty(partialAttribute)) { //XSD validation should have guarenteed that the attribute value can only be true or false Debug.Assert(partialAttribute == "true" || partialAttribute == "false"); isPartial = Convert.ToBoolean(partialAttribute, System.Globalization.CultureInfo.InvariantCulture); } //Create an ComplexTypeMapping to hold the information for Type mapping. StorageComplexTypeMapping typeMapping = new StorageComplexTypeMapping(isPartial); if (type != null) { typeMapping.AddType(type as ComplexType); } else { Debug.Assert(nav.LocalName == StorageMslConstructs.ComplexTypeMappingElement); string typeName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ComplexTypeMappingTypeNameAttribute); int index = typeName.IndexOf(StorageMslConstructs.TypeNameSperator); string currentTypeName = null; do { if (index != -1) { currentTypeName = typeName.Substring(0, index); typeName = typeName.Substring(index + 1, (typeName.Length - (index + 1))); } else { currentTypeName = typeName; typeName = string.Empty; } int isTypeOfIndex = currentTypeName.IndexOf(StorageMslConstructs.IsTypeOf, StringComparison.Ordinal); if (isTypeOfIndex == 0) { currentTypeName = currentTypeName.Substring(StorageMslConstructs.IsTypeOf.Length, (currentTypeName.Length - (StorageMslConstructs.IsTypeOf.Length + 1))); currentTypeName = GetAliasResolvedValue(currentTypeName); } else { currentTypeName = GetAliasResolvedValue(currentTypeName); } ComplexType complexType; this.EdmItemCollection.TryGetItem(currentTypeName, out complexType); if (complexType == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_Complex_Type_1, currentTypeName, StorageMappingErrorCode.InvalidComplexType, m_sourceLocation, (IXmlLineInfo)nav, m_parsingErrors); index = typeName.IndexOf(StorageMslConstructs.TypeNameSperator); continue; } if (isTypeOfIndex == 0) { typeMapping.AddIsOfType(complexType); } else { typeMapping.AddType(complexType); } index = typeName.IndexOf(StorageMslConstructs.TypeNameSperator); } while (typeName.Length != 0); } //Now load the children of ComplexTypeMapping if (nav.MoveToChild(XPathNodeType.Element)) { do { EdmType containerType = typeMapping.GetOwnerType(StorageMappingItemLoader.GetAttributeValue(nav.Clone(), StorageMslConstructs.ComplexPropertyNameAttribute)); switch (nav.LocalName) { case StorageMslConstructs.ScalarPropertyElement: StorageScalarPropertyMapping scalarMap = LoadScalarPropertyMapping(nav.Clone(), containerType, tableType); //ScalarMap can be null in case of invalid MSL files if (scalarMap != null) { typeMapping.AddProperty(scalarMap); } break; case StorageMslConstructs.ComplexPropertyElement: StorageComplexPropertyMapping complexMap = LoadComplexPropertyMapping(nav.Clone(), containerType, tableType); //complexMap can be null in case of invalid maps if (complexMap != null) { typeMapping.AddProperty(complexMap); } break; case StorageMslConstructs.ConditionElement: StorageConditionPropertyMapping conditionMap = LoadConditionPropertyMapping(nav.Clone(), containerType, tableType); if (conditionMap != null) { typeMapping.AddConditionProperty(conditionMap); } break; default: throw System.Data.Entity.Error.NotSupported(); } } while (nav.MoveToNext(XPathNodeType.Element)); } return typeMapping; } /// /// The method loads the EndProperty mapping /// into the internal datastructures. /// /// /// /// ///private StorageEndPropertyMapping LoadEndPropertyMapping(XPathNavigator nav, AssociationEndMember end, EntityType tableType) { //FutureEnhancement : Change End Property Mapping to not derive from // StoragePropertyMapping StorageEndPropertyMapping endMapping = new StorageEndPropertyMapping(null); endMapping.EndMember = end; nav.MoveToChild(XPathNodeType.Element); do { switch (nav.LocalName) { case StorageMslConstructs.ScalarPropertyElement: RefType endRef = end.TypeUsage.EdmType as RefType; Debug.Assert(endRef != null); EntityTypeBase containerType = endRef.ElementType; StorageScalarPropertyMapping scalarMap = LoadScalarPropertyMapping(nav.Clone(), containerType, tableType); //Scalar Property Mapping can be null //in case of invalid MSL files. if (scalarMap != null) { //Make sure that the properties mapped as part of EndProperty maps are the key properties. //If any other property is mapped, we should raise an error. if (!containerType.KeyMembers.Contains(scalarMap.EdmProperty)) { IXmlLineInfo navLineInfo = (IXmlLineInfo)nav; AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_EndProperty_1, scalarMap.EdmProperty.Name, StorageMappingErrorCode.InvalidEdmMember, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } endMapping.AddProperty(scalarMap); } break; default: Debug.Fail("XSD validation should have ensured that End EdmProperty Maps only have Schalar properties"); break; } } while (nav.MoveToNext(XPathNodeType.Element)); return endMapping; } /// /// The method loads the ConditionProperty mapping /// into the internal datastructures. /// /// /// /// ///private StorageConditionPropertyMapping LoadConditionPropertyMapping(XPathNavigator nav, EdmType containerType, EntityType tableType) { //Get the CDM side property name. string propertyName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ConditionNameAttribute); //Get the Store side property name from Storeside string columnName = GetAliasResolvedAttributeValue(nav.Clone(), StorageMslConstructs.ConditionColumnNameAttribute); IXmlLineInfo navLineInfo = (IXmlLineInfo)nav; //Either the property name or column name can be specified but both can not be. if ((propertyName != null) && (columnName != null)) { AddToSchemaErrors(Strings.Mapping_InvalidContent_ConditionMapping_Both_Members_0, StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } if ((propertyName == null) && (columnName == null)) { AddToSchemaErrors(Strings.Mapping_InvalidContent_ConditionMapping_Either_Members_0, StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } EdmProperty member = null; //Get the CDM EdmMember reprsented by the name specified. if (propertyName != null) { EdmMember tempMember; //If container type is null that means we have not found the member in any of the IsOfTypes. if (containerType != null) { ((StructuralType)containerType).Members.TryGetValue(propertyName, false /*ignoreCase*/, out tempMember); member = tempMember as EdmProperty; } } //Get the column EdmMember represented by the column name specified EdmProperty columnMember = null; if (columnName != null) { tableType.Properties.TryGetValue(columnName, false, out columnMember); } //Get the member for which the condition is being specified EdmProperty conditionMember = (columnMember != null) ? columnMember : member; if (conditionMember == null) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_ConditionMapping_InvalidMember_1, ((columnName != null) ? columnName : propertyName), StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } Nullable isNullValue = null; object value = null; //Get the attribute value for IsNull attribute string isNullAttribute = StorageMappingItemLoader.GetAttributeValue(nav.Clone(), StorageMslConstructs.ConditionIsNullAttribute); //Get strongly Typed value if the condition was specified for a specific condition EdmType edmType = conditionMember.TypeUsage.EdmType; if (Helper.IsPrimitiveType(edmType)) { //Decide if the member is of a type that we would allow a condition on. //First convert the type to C space, if this is a condition in s space( before checking this). TypeUsage cspaceTypeUsage; if (conditionMember.DeclaringType.DataSpace == DataSpace.SSpace) { cspaceTypeUsage = StoreItemCollection.StoreProviderManifest.GetEdmType(conditionMember.TypeUsage); if (cspaceTypeUsage == null) { AddToSchemaErrorWithMessage(System.Data.Entity.Strings.Mapping_ProviderReturnsNullType(conditionMember.Name), StorageMappingErrorCode.MappingStoreProviderReturnsNullEdmType, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } } else { cspaceTypeUsage = conditionMember.TypeUsage; } PrimitiveType memberType = ((PrimitiveType)cspaceTypeUsage.EdmType); Type clrMemberType = memberType.ClrEquivalentType; PrimitiveTypeKind primitiveTypeKind = memberType.PrimitiveTypeKind; //Only a subset of primitive types can be used in Conditions that are specified over values. //IsNull conditions can be specified on any primitive types if ((isNullAttribute == null) && !IsTypeSupportedForCondition(primitiveTypeKind)) { AddToSchemaErrorWithMemberAndStructure(Strings.Mapping_InvalidContent_ConditionMapping_InvalidPrimitiveTypeKind_2, conditionMember.Name, memberType.FullName, StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } Debug.Assert(clrMemberType != null, "Scalar Types should have associated clr type"); //If the value is not compatible with the type, just add an error and return if(!StorageMappingItemLoader.TryGetTypedAttributeValue(nav.Clone(), StorageMslConstructs.ConditionValueAttribute, clrMemberType, m_sourceLocation, m_parsingErrors, out value)) { return null; } } else if (Helper.IsEnumType(edmType)) { // Enumeration type - get the actual value value = StorageMappingItemLoader.GetEnumAttributeValue(nav.Clone(), StorageMslConstructs.ConditionValueAttribute, (EnumType)edmType, m_sourceLocation, m_parsingErrors); } else { // Since NullableComplexTypes are not being supported, // we don't allow conditions on complex types AddToSchemaErrors(Strings.Mapping_InvalidContent_ConditionMapping_NonScalar_0, StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } //Either Value or NotNull need to be specifid on the condition mapping but not both if ((isNullAttribute != null) && (value != null)) { AddToSchemaErrors(Strings.Mapping_InvalidContent_ConditionMapping_Both_Values_0, StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } if ((isNullAttribute == null) && (value == null)) { AddToSchemaErrors(Strings.Mapping_InvalidContent_ConditionMapping_Either_Values_0, StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } if (isNullAttribute != null) { //XSD validation should have guarenteed that the attribute value can only be true or false Debug.Assert(isNullAttribute == "true" || isNullAttribute == "false"); isNullValue = Convert.ToBoolean(isNullAttribute, System.Globalization.CultureInfo.InvariantCulture); } if (columnMember != null && (columnMember.IsStoreGeneratedComputed || columnMember.IsStoreGeneratedIdentity)) { AddToSchemaErrorsWithMemberInfo(Strings.Mapping_InvalidContent_ConditionMapping_Computed, columnMember.Name, StorageMappingErrorCode.ConditionError, m_sourceLocation, navLineInfo, m_parsingErrors); return null; } StorageConditionPropertyMapping conditionPropertyMapping = new StorageConditionPropertyMapping(member, columnMember, value, isNullValue); return conditionPropertyMapping; } internal static bool IsTypeSupportedForCondition(PrimitiveTypeKind primitiveTypeKind) { switch (primitiveTypeKind) { case PrimitiveTypeKind.Boolean: case PrimitiveTypeKind.Byte: case PrimitiveTypeKind.Int16: case PrimitiveTypeKind.Int32: case PrimitiveTypeKind.Int64: case PrimitiveTypeKind.String: case PrimitiveTypeKind.SByte: return true; case PrimitiveTypeKind.Binary: case PrimitiveTypeKind.DateTime: case PrimitiveTypeKind.Time: case PrimitiveTypeKind.DateTimeOffset: case PrimitiveTypeKind.Double: case PrimitiveTypeKind.Guid: case PrimitiveTypeKind.Single: case PrimitiveTypeKind.Decimal: return false; default: Debug.Fail("New primitive type kind added?"); return false; } } private static XmlSchemaSet GetOrCreateSchemaSet() { if (s_mappingXmlSchema == null) { //Get the xsd stream for CS MSL Xsd. XmlSchemaSet set = new XmlSchemaSet(); AddResourceXsdToSchemaSet(set, StorageMslConstructs.ResourceXsdNameV1); AddResourceXsdToSchemaSet(set, StorageMslConstructs.ResourceXsdNameV2); System.Threading.Interlocked.CompareExchange(ref s_mappingXmlSchema, set, null); } return s_mappingXmlSchema; } private static void AddResourceXsdToSchemaSet(XmlSchemaSet set, string resourceName) { using (XmlReader xsdReader = System.Data.Common.DbProviderServices.GetXmlResource(resourceName)) { XmlSchema xmlSchema = XmlSchema.Read(xsdReader, null); set.Add(xmlSchema); } } /// /// Throws a new MappingException giving out the line number and /// File Name where the error in Mapping specification is present. /// /// /// /// /// /// Error Collection where the parsing errors are collected private static void AddToSchemaErrors(string message, StorageMappingErrorCode errorCode, string location, IXmlLineInfo lineInfo, IListparsingErrors) { EdmSchemaError error = new EdmSchemaError(message, (int)errorCode, EdmSchemaErrorSeverity.Error, location, lineInfo.LineNumber, lineInfo.LinePosition); parsingErrors.Add(error); } private static EdmSchemaError AddToSchemaErrorsWithMemberInfo(Func
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlCharCheckingReader.cs
- ReflectTypeDescriptionProvider.cs
- LocatorManager.cs
- CompModSwitches.cs
- MarkerProperties.cs
- SRef.cs
- SqlCacheDependencyDatabaseCollection.cs
- TypeDescriptionProviderAttribute.cs
- WSFederationHttpSecurity.cs
- ObjectListDataBindEventArgs.cs
- ConfigXmlWhitespace.cs
- FileCodeGroup.cs
- DataTableMapping.cs
- SQLConvert.cs
- FilterException.cs
- OleDbCommand.cs
- OrderPreservingSpoolingTask.cs
- TextServicesHost.cs
- DiscoveryClient.cs
- Roles.cs
- DbConnectionFactory.cs
- safemediahandle.cs
- TemplatedMailWebEventProvider.cs
- SafeRegistryHandle.cs
- DataGridViewRow.cs
- TemplatePagerField.cs
- BaseConfigurationRecord.cs
- DesignBindingConverter.cs
- IncomingWebRequestContext.cs
- ReflectionTypeLoadException.cs
- DataGridViewUtilities.cs
- PrintSystemException.cs
- ELinqQueryState.cs
- SymbolDocumentInfo.cs
- DebugView.cs
- _StreamFramer.cs
- XmlDataContract.cs
- SerializationAttributes.cs
- TextElementEditingBehaviorAttribute.cs
- HtmlDocument.cs
- DataGridViewAddColumnDialog.cs
- StateRuntime.cs
- RegionInfo.cs
- ZipIOCentralDirectoryDigitalSignature.cs
- Processor.cs
- MenuItemCollection.cs
- WebPartExportVerb.cs
- MetaModel.cs
- SchemaManager.cs
- DynamicILGenerator.cs
- RootBrowserWindow.cs
- Soap.cs
- StringBuilder.cs
- HttpListenerResponse.cs
- EventSetter.cs
- CollectionContainer.cs
- XmlAnyElementAttributes.cs
- DateTimePicker.cs
- XmlSerializerAssemblyAttribute.cs
- OleStrCAMarshaler.cs
- Expressions.cs
- SingleConverter.cs
- SpellerHighlightLayer.cs
- QueryStatement.cs
- WriterOutput.cs
- SpecialNameAttribute.cs
- SqlDataSourceConfigureFilterForm.cs
- FlowDocumentPageViewerAutomationPeer.cs
- DateTimeValueSerializer.cs
- RankException.cs
- HighlightComponent.cs
- SqlVisitor.cs
- OletxDependentTransaction.cs
- remotingproxy.cs
- Monitor.cs
- CheckBoxField.cs
- _ProxyChain.cs
- CommandBinding.cs
- ParentUndoUnit.cs
- DataServiceCollectionOfT.cs
- ZipIOLocalFileHeader.cs
- FormClosedEvent.cs
- SiteMapDataSourceView.cs
- _ListenerRequestStream.cs
- PropertyChangingEventArgs.cs
- WasAdminWrapper.cs
- ListManagerBindingsCollection.cs
- Positioning.cs
- SqlStatistics.cs
- IMembershipProvider.cs
- RelationshipNavigation.cs
- AuthenticationConfig.cs
- Directory.cs
- GestureRecognizer.cs
- NameValuePermission.cs
- RectConverter.cs
- LicFileLicenseProvider.cs
- EventLogPermissionEntry.cs
- EndpointFilterProvider.cs
- Activator.cs