//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data.Common.Utils; using System.Data.Entity; using System.Data.Mapping.Update.Internal; using System.Data.Mapping.ViewGeneration; using System.Data.Metadata.Edm; using System.Diagnostics; using System.Linq; using System.Runtime.Versioning; using System.Xml; using som = System.Data.EntityModel.SchemaObjectModel; namespace System.Data.Mapping { using OfTypeQVCacheKey = Pair>; /// /// Class for representing a collection of items in Storage Mapping( CS Mapping) space. /// [CLSCompliant(false)] public partial class StorageMappingItemCollection : MappingItemCollection { #region Fields //EdmItemCollection that is associated with the MSL Loader. private EdmItemCollection m_edmCollection; //StoreItemCollection that is associated with the MSL Loader. private StoreItemCollection m_storeItemCollection; private ViewDictionary m_viewDictionary; private double m_mappingVersion = XmlConstants.UndefinedVersion; private MetadataWorkspace m_workspace; // In this version, we won't allow same types in CSpace to map to different types in store. If the same type // need to be reused, the store type must be the same. To keep track of this, we need to keep track of the member // mapping across maps to make sure they are mapped to the same store side. // The first TypeUsage in the KeyValuePair stores the store equivalent type for the cspace member type and the second // one store the actual store type to which the member is mapped to. // For e.g. If the CSpace member of type Edm.Int32 maps to a sspace member of type SqlServer.bigint, then the KeyValuePair // for the cspace member will contain SqlServer.int (store equivalent for Edm.Int32) and SqlServer.bigint (Actual store type // to which the member was mapped to) private Dictionary> m_memberMappings = new Dictionary>(); private ViewLoader _viewLoader; internal enum InterestingMembersKind { RequiredOriginalValueMembers, // legacy - used by the obsolete GetRequiredOriginalValueMembers FullUpdate, // Interesting members in case of full update scenario PartialUpdate // Interesting members in case of partial update scenario }; private ConcurrentDictionary, ReadOnlyCollection> _cachedInterestingMembers = new ConcurrentDictionary, ReadOnlyCollection>(); #endregion #region Constructors /// /// constructor that takes in a list of folder or files or a mix of both and /// creates metadata for mapping in all the files. /// /// /// /// [ResourceExposure(ResourceScope.Machine)] //Exposes the file path names which are a Machine resource [ResourceConsumption(ResourceScope.Machine)] //For MetadataArtifactLoader.CreateCompositeFromFilePaths method call but we do not create the file paths in this method [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "edm")] public StorageMappingItemCollection(EdmItemCollection edmCollection, StoreItemCollection storeCollection, params string[] filePaths) : base(DataSpace.CSSpace) { EntityUtil.CheckArgumentNull(edmCollection, "edmCollection"); EntityUtil.CheckArgumentNull(storeCollection, "storeCollection"); EntityUtil.CheckArgumentNull(filePaths, "filePaths"); this.m_edmCollection = edmCollection; this.m_storeItemCollection = storeCollection; // Wrap the file paths in instances of the MetadataArtifactLoader class, which provides // an abstraction and a uniform interface over a diverse set of metadata artifacts. // MetadataArtifactLoader composite = null; List readers = null; try { composite = MetadataArtifactLoader.CreateCompositeFromFilePaths(filePaths, XmlConstants.CSSpaceSchemaExtension); readers = composite.CreateReaders(DataSpace.CSSpace); this.Init(edmCollection, storeCollection, readers, composite.GetPaths(DataSpace.CSSpace), true /*throwOnError*/); } finally { if (readers != null) { Helper.DisposeXmlReaders(readers); } } } /// /// constructor that takes in a list of XmlReaders and creates metadata for mapping /// in all the files. /// /// The edm metadata collection that this mapping is to use /// The store metadata collection that this mapping is to use /// The XmlReaders to load mapping from [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "edm")] public StorageMappingItemCollection(EdmItemCollection edmCollection, StoreItemCollection storeCollection, IEnumerable xmlReaders) : base(DataSpace.CSSpace) { EntityUtil.CheckArgumentNull(xmlReaders, "xmlReaders"); MetadataArtifactLoader composite = MetadataArtifactLoader.CreateCompositeFromXmlReaders(xmlReaders); this.Init(edmCollection, storeCollection, composite.GetReaders(), // filter out duplicates composite.GetPaths(), true /* throwOnError*/); } /// /// constructor that takes in a list of XmlReaders and creates metadata for mapping /// in all the files. /// /// The edm metadata collection that this mapping is to use /// The store metadata collection that this mapping is to use /// Mapping URIs /// The XmlReaders to load mapping from /// a list of errors for each file loaded // referenced by System.Data.Entity.Design.dll [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] internal StorageMappingItemCollection(EdmItemCollection edmCollection, StoreItemCollection storeCollection, IEnumerable xmlReaders, List filePaths, out IList errors) : base(DataSpace.CSSpace) { // we will check the parameters for this internal ctor becuase // it is pretty much publicly exposed through the MetadataItemCollectionFactory // in System.Data.Entity.Design EntityUtil.CheckArgumentNull(xmlReaders, "xmlReaders"); EntityUtil.CheckArgumentContainsNull(ref xmlReaders, "xmlReaders"); // filePaths is allowed to be null errors = this.Init(edmCollection, storeCollection, xmlReaders, filePaths, false /*throwOnError*/); } /// /// constructor that takes in a list of XmlReaders and creates metadata for mapping /// in all the files. /// /// The edm metadata collection that this mapping is to use /// The store metadata collection that this mapping is to use /// Mapping URIs /// The XmlReaders to load mapping from [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] internal StorageMappingItemCollection(EdmItemCollection edmCollection, StoreItemCollection storeCollection, IEnumerable xmlReaders, List filePaths) : base(DataSpace.CSSpace) { this.Init(edmCollection, storeCollection, xmlReaders, filePaths, true /*throwOnError*/); } /// /// Initializer that takes in a list of XmlReaders and creates metadata for mapping /// in all the files. /// /// The edm metadata collection that this mapping is to use /// The store metadata collection that this mapping is to use /// Mapping URIs /// The XmlReaders to load mapping from /// a list of errors for each file loaded private IList Init(EdmItemCollection edmCollection, StoreItemCollection storeCollection, IEnumerable xmlReaders, List filePaths, bool throwOnError) { EntityUtil.CheckArgumentNull(xmlReaders, "xmlReaders"); EntityUtil.CheckArgumentNull(edmCollection, "edmCollection"); EntityUtil.CheckArgumentNull(storeCollection, "storeCollection"); this.m_edmCollection = edmCollection; this.m_storeItemCollection = storeCollection; Dictionary userDefinedQueryViewsDict; Dictionary userDefinedQueryViewsOfTypeDict; this.m_viewDictionary = new ViewDictionary(this, out userDefinedQueryViewsDict, out userDefinedQueryViewsOfTypeDict); List errors = new List(); if(this.m_edmCollection.EdmVersion != XmlConstants.UndefinedVersion && this.m_storeItemCollection.StoreSchemaVersion != XmlConstants.UndefinedVersion && this.m_edmCollection.EdmVersion != this.m_storeItemCollection.StoreSchemaVersion) { errors.Add( new EdmSchemaError( Strings.Mapping_DifferentEdmStoreVersion, (int)StorageMappingErrorCode.MappingDifferentEdmStoreVersion, EdmSchemaErrorSeverity.Error)); } else { double expectedVersion = this.m_edmCollection.EdmVersion != XmlConstants.UndefinedVersion ? this.m_edmCollection.EdmVersion : this.m_storeItemCollection.StoreSchemaVersion; errors.AddRange(LoadItems(xmlReaders, filePaths, userDefinedQueryViewsDict, userDefinedQueryViewsOfTypeDict, expectedVersion)); } Debug.Assert(errors != null); if (errors.Count > 0 && throwOnError) { if (!System.Data.Common.Utils.MetadataHelper.CheckIfAllErrorsAreWarnings(errors)) { // NOTE: not using Strings.InvalidSchemaEncountered because it will truncate the errors list. throw new MappingException( String.Format(System.Globalization.CultureInfo.CurrentCulture, EntityRes.GetString(EntityRes.InvalidSchemaEncountered), Helper.CombineErrorMessage(errors))); } } return errors; } #endregion Constructors internal MetadataWorkspace Workspace { get { if (m_workspace == null) { m_workspace = new MetadataWorkspace(); m_workspace.RegisterItemCollection(m_edmCollection); m_workspace.RegisterItemCollection(m_storeItemCollection); m_workspace.RegisterItemCollection(this); } return m_workspace; } } /// /// Return the EdmItemCollection associated with the Mapping Collection /// internal EdmItemCollection EdmItemCollection { get { return this.m_edmCollection; } } /// /// Version of this StorageMappingItemCollection represents. /// public double MappingVersion { get { return this.m_mappingVersion; } } /// /// Return the StoreItemCollection associated with the Mapping Collection /// internal StoreItemCollection StoreItemCollection { get { return this.m_storeItemCollection; } } /// /// Search for a Mapping metadata with the specified type key. /// /// identity of the type /// The dataspace that the type for which map needs to be returned belongs to /// true for case-insensitive lookup /// Thrown if mapping space is not valid internal override Map GetMap(string identity, DataSpace typeSpace, bool ignoreCase) { EntityUtil.CheckArgumentNull(identity, "identity"); if (typeSpace != DataSpace.CSpace) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Mapping_Storage_InvalidSpace(typeSpace)); } return GetItem(identity, ignoreCase); } /// /// Search for a Mapping metadata with the specified type key. /// /// identity of the type /// The dataspace that the type for which map needs to be returned belongs to /// true for case-insensitive lookup /// /// Returns false if no match found. internal override bool TryGetMap(string identity, DataSpace typeSpace, bool ignoreCase, out Map map) { if (typeSpace != DataSpace.CSpace) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Mapping_Storage_InvalidSpace(typeSpace)); } return TryGetItem(identity, ignoreCase, out map); } /// /// Search for a Mapping metadata with the specified type key. /// /// identity of the type /// The dataspace that the type for which map needs to be returned belongs to /// Thrown if mapping space is not valid internal override Map GetMap(string identity, DataSpace typeSpace) { return this.GetMap(identity, typeSpace, false /*ignoreCase*/); } /// /// Search for a Mapping metadata with the specified type key. /// /// identity of the type /// The dataspace that the type for which map needs to be returned belongs to /// /// Returns false if no match found. internal override bool TryGetMap(string identity, DataSpace typeSpace, out Map map) { return this.TryGetMap(identity, typeSpace, false /*ignoreCase*/, out map); } /// /// Search for a Mapping metadata with the specified type key. /// /// internal override Map GetMap(GlobalItem item) { EntityUtil.CheckArgumentNull(item, "item"); DataSpace typeSpace = item.DataSpace; if (typeSpace != DataSpace.CSpace) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Mapping_Storage_InvalidSpace(typeSpace)); } return this.GetMap(item.Identity, typeSpace); } /// /// Search for a Mapping metadata with the specified type key. /// /// /// /// Returns false if no match found. internal override bool TryGetMap(GlobalItem item, out Map map) { if (item == null) { map = null; return false; } DataSpace typeSpace = item.DataSpace; if (typeSpace != DataSpace.CSpace) { map = null; return false; } return this.TryGetMap(item.Identity, typeSpace, out map); } /// /// This method /// - generates views from the mapping elements in the collection; /// - does not process user defined views - these are processed during mapping collection loading; /// - does not cache generated views in the mapping collection. /// The main purpose is design-time view validation and generation. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] // referenced by System.Data.Entity.Design.dll internal Dictionary GenerateEntitySetViews(out IList errors) { Dictionary esqlViews = new Dictionary(); errors = new List(); foreach (var mapping in GetItems()) { var entityContainerMapping = mapping as StorageEntityContainerMapping; if (entityContainerMapping != null) { // If there are no entity set maps, don't call the view generation process. if (!entityContainerMapping.HasViews) { return esqlViews; } // If entityContainerMapping contains only query views, then add a warning to the errors and continue to next mapping. if (!entityContainerMapping.HasMappingFragments()) { Debug.Assert(2088 == (int)StorageMappingErrorCode.MappingAllQueryViewAtCompileTime, "Please change the ERRORCODE_MAPPINGALLQUERYVIEWATCOMPILETIME value as well"); errors.Add(new EdmSchemaError( Strings.Mapping_AllQueryViewAtCompileTime(entityContainerMapping.Identity), (int)StorageMappingErrorCode.MappingAllQueryViewAtCompileTime, EdmSchemaErrorSeverity.Warning)); } else { ViewGenResults viewGenResults = ViewgenGatekeeper.GenerateViewsFromMapping(entityContainerMapping, new ConfigViewGenerator() { GenerateEsql = true }); if (viewGenResults.HasErrors) { ((List)errors).AddRange(viewGenResults.Errors); } KeyToListMap extentMappingViews = viewGenResults.Views; foreach (KeyValuePair> extentViewPair in extentMappingViews.KeyValuePairs) { List generatedViews = extentViewPair.Value; // Multiple Views are returned for an extent but the first view // is the only one that we will use for now. In the future, // we might start using the other views which are per type within an extent. esqlViews.Add(extentViewPair.Key, generatedViews[0].eSQL); } } } } return esqlViews; } #region Get interesting members /// /// Return members for MetdataWorkspace.GetRequiredOriginalValueMembers() and MetdataWorkspace.GetRelevantMembersForUpdate() methods. /// /// An EntitySet belonging to the C-Space. Must not be null. /// An EntityType that participates in the given EntitySet. Must not be null. /// Scenario the members should be returned for. /// ReadOnlyCollection of interesting members for the requested scenario (). internal ReadOnlyCollection GetInterestingMembers(EntitySetBase entitySet, EntityTypeBase entityType, InterestingMembersKind interestingMembersKind) { Debug.Assert(entitySet != null, "entitySet != null"); Debug.Assert(entityType != null, "entityType != null"); var key = new Tuple(entitySet, entityType, interestingMembersKind); return _cachedInterestingMembers.GetOrAdd(key, FindInterestingMembers(entitySet, entityType, interestingMembersKind)); } /// /// Finds interesting members for MetdataWorkspace.GetRequiredOriginalValueMembers() and MetdataWorkspace.GetRelevantMembersForUpdate() methods /// for the given and . /// /// An EntitySet belonging to the C-Space. Must not be null. /// An EntityType that participates in the given EntitySet. Must not be null. /// Scenario the members should be returned for. /// ReadOnlyCollection of interesting members for the requested scenario (). private ReadOnlyCollection FindInterestingMembers(EntitySetBase entitySet, EntityTypeBase entityType, InterestingMembersKind interestingMembersKind) { Debug.Assert(entitySet != null, "entitySet != null"); Debug.Assert(entityType != null, "entityType != null"); var interestingMembers = new List(); foreach (var storageTypeMapping in MappingMetadataHelper.GetMappingsForEntitySetAndSuperTypes(this, entitySet.EntityContainer, entitySet, entityType)) { StorageAssociationTypeMapping associationTypeMapping = storageTypeMapping as StorageAssociationTypeMapping; if (associationTypeMapping != null) { FindInterestingAssociationMappingMembers(associationTypeMapping, interestingMembers); } else { Debug.Assert(storageTypeMapping is StorageEntityTypeMapping, "StorageEntityTypeMapping expected."); FindInterestingEntityMappingMembers((StorageEntityTypeMapping)storageTypeMapping, interestingMembersKind, interestingMembers); } } // For backwards compatibility we don't return foreign keys from the obsolete MetadataWorkspace.GetRequiredOriginalValueMembers() method if (interestingMembersKind != InterestingMembersKind.RequiredOriginalValueMembers) { FindForeignKeyProperties(entitySet, entityType, interestingMembers); } foreach (var functionMappings in MappingMetadataHelper .GetModificationFunctionMappingsForEntitySetAndType(this, entitySet.EntityContainer, entitySet, entityType) .Where(functionMappings => functionMappings.UpdateFunctionMapping != null)) { FindInterestingFunctionMappingMembers(functionMappings, interestingMembersKind, ref interestingMembers); } Debug.Assert(interestingMembers != null, "interestingMembers must never be null."); return new ReadOnlyCollection(interestingMembers.Distinct().ToList()); } /// /// Finds members participating in the assocciation and adds them to the . /// /// Association type mapping. Must not be null. /// The list the interesting members (if any) will be added to. Must not be null. private static void FindInterestingAssociationMappingMembers(StorageAssociationTypeMapping associationTypeMapping, List interestingMembers) { Debug.Assert(associationTypeMapping != null, "entityTypeMapping != null"); Debug.Assert(interestingMembers != null, "interestingMembers != null"); //(2) Ends participating in association are "interesting" interestingMembers.AddRange( associationTypeMapping .MappingFragments .SelectMany(m => m.AllProperties) .OfType() .Select(epm => epm.EndMember)); } /// /// Finds interesting entity properties - primary keys (if requested), properties (including complex properties and nested properties) /// with concurrency mode set to fixed and C-Side condition members and adds them to the . /// /// Entity type mapping. Must not be null. /// Scenario the members should be returned for. /// The list the interesting members (if any) will be added to. Must not be null. private static void FindInterestingEntityMappingMembers(StorageEntityTypeMapping entityTypeMapping, InterestingMembersKind interestingMembersKind, List interestingMembers) { Debug.Assert(entityTypeMapping != null, "entityTypeMapping != null"); Debug.Assert(interestingMembers != null, "interestingMembers != null"); foreach (var propertyMapping in entityTypeMapping.MappingFragments.SelectMany(mf => mf.AllProperties)) { StorageScalarPropertyMapping scalarPropMapping = propertyMapping as StorageScalarPropertyMapping; StorageComplexPropertyMapping complexPropMapping = propertyMapping as StorageComplexPropertyMapping; StorageConditionPropertyMapping conditionMapping = propertyMapping as StorageConditionPropertyMapping; Debug.Assert(!(propertyMapping is StorageEndPropertyMapping), "association mapping properties should be handled elsewhere."); Debug.Assert(scalarPropMapping != null || complexPropMapping != null || conditionMapping != null, "Unimplemented property mapping"); //scalar property if (scalarPropMapping != null && scalarPropMapping.EdmProperty != null) { // (0) if a member is part of the key it is interesting if (MetadataHelper.IsPartOfEntityTypeKey(scalarPropMapping.EdmProperty)) { // For backwards compatibility we do return primary keys from the obsolete MetadataWorkspace.GetRequiredOriginalValueMembers() method if (interestingMembersKind == InterestingMembersKind.RequiredOriginalValueMembers) { interestingMembers.Add(scalarPropMapping.EdmProperty); } } //(3) if a scalar property has Fixed concurrency mode then it is "interesting" else if (MetadataHelper.GetConcurrencyMode(scalarPropMapping.EdmProperty) == ConcurrencyMode.Fixed) { interestingMembers.Add(scalarPropMapping.EdmProperty); } } else if (complexPropMapping != null) { // (7) All complex members - partial update scenarios only // (3.1) The complex property or its one of its children has fixed concurrency mode if (interestingMembersKind == InterestingMembersKind.PartialUpdate || MetadataHelper.GetConcurrencyMode(complexPropMapping.EdmProperty) == ConcurrencyMode.Fixed || HasFixedConcurrencyModeInAnyChildProperty(complexPropMapping)) { interestingMembers.Add(complexPropMapping.EdmProperty); } } else if (conditionMapping != null) { //(1) C-Side condition members are 'interesting' if (conditionMapping.EdmProperty != null) { interestingMembers.Add(conditionMapping.EdmProperty); } } } } /// /// Recurses down the complex property to find whether any of the nseted properties has concurrency mode set to "Fixed" /// /// Complex property mapping. Must not be null. /// true if any of the descendant properties has concurrency mode set to "Fixed". Otherwise false. private static bool HasFixedConcurrencyModeInAnyChildProperty(StorageComplexPropertyMapping complexMapping) { Debug.Assert(complexMapping != null, "complexMapping != null"); foreach (StoragePropertyMapping propertyMapping in complexMapping.TypeMappings.SelectMany(m => m.AllProperties)) { StorageScalarPropertyMapping childScalarPropertyMapping = propertyMapping as StorageScalarPropertyMapping; StorageComplexPropertyMapping childComplexPropertyMapping = propertyMapping as StorageComplexPropertyMapping; Debug.Assert(childScalarPropertyMapping != null || childComplexPropertyMapping != null, "Unimplemented property mapping for complex property"); //scalar property and has Fixed CC mode if (childScalarPropertyMapping != null && MetadataHelper.GetConcurrencyMode(childScalarPropertyMapping.EdmProperty) == ConcurrencyMode.Fixed) { return true; } // Complex Prop and sub-properties or itself has fixed CC mode else if (childComplexPropertyMapping != null && (MetadataHelper.GetConcurrencyMode(childComplexPropertyMapping.EdmProperty) == ConcurrencyMode.Fixed || HasFixedConcurrencyModeInAnyChildProperty(childComplexPropertyMapping))) { return true; } } return false; } /// /// Finds foreign key properties and adds them to the . /// /// Entity set relates to. Must not be null. /// Entity type for which to find foreign key properties. Must not be null. /// The list the interesting members (if any) will be added to. Must not be null. private void FindForeignKeyProperties(EntitySetBase entitySetBase, EntityTypeBase entityType, List interestingMembers) { var entitySet = entitySetBase as EntitySet; if (entitySet != null && entitySet.HasForeignKeyRelationships) { // (6) Foreign keys // select all foreign key properties defined on the entityType and all its ancestors interestingMembers.AddRange( MetadataHelper.GetTypeAndParentTypesOf(entityType, this.m_edmCollection, true) .SelectMany(e => ((EntityType)e).Properties) .Where(p => entitySet.ForeignKeyDependents.SelectMany(fk => fk.Item2.ToProperties).Contains(p))); } } /// /// Finds interesting members for modification functions mapped to stored procedures and adds them to the . /// /// Modification function mapping. Must not be null. /// Update scenario the members will be used in (in general - partial update vs. full update). /// private static void FindInterestingFunctionMappingMembers(StorageEntityTypeModificationFunctionMapping functionMappings, InterestingMembersKind interestingMembersKind, ref List interestingMembers) { Debug.Assert(functionMappings != null && functionMappings.UpdateFunctionMapping != null, "Expected function mapping fragment with non-null update function mapping"); Debug.Assert(interestingMembers != null, "interestingMembers != null"); // for partial update scenarios (e.g. EntityDataSourceControl) all members are interesting otherwise the data may be corrupt. // See bugs #272992 and #124460 in DevDiv database for more details. For full update scenarios and the obsolete // MetadataWorkspace.GetRequiredOriginalValueMembers() metod we return only members with Version set to "Original". if (interestingMembersKind == InterestingMembersKind.PartialUpdate) { // (5) Members included in Update ModificationFunction interestingMembers.AddRange(functionMappings.UpdateFunctionMapping.ParameterBindings.Select(p => p.MemberPath.Members.Last())); } else { //(4) Members in update ModificationFunction with Version="Original" are "interesting" // This also works when you have complex-types (4.1) Debug.Assert( interestingMembersKind == InterestingMembersKind.FullUpdate || interestingMembersKind == InterestingMembersKind.RequiredOriginalValueMembers, "Unexpected kind of interesting members - if you changed the InterestingMembersKind enum type update this code accordingly"); foreach (var parameterBinding in functionMappings.UpdateFunctionMapping.ParameterBindings.Where(p => !p.IsCurrent)) { //Last is the root element (with respect to the Entity) //For example, Entity1={ // S1, // C1{S2, // C2{ S3, S4 } // }, // S5} // if S4 matches (i.e. C1.C2.S4), then it returns C1 //because internally the list is [S4][C2][C1] interestingMembers.Add(parameterBinding.MemberPath.Members.Last()); } } } #endregion /// /// Calls the view dictionary to load the view, see detailed comments in the view dictionary class. /// internal GeneratedView GetGeneratedView(EntitySetBase extent, MetadataWorkspace workspace) { return this.m_viewDictionary.GetGeneratedView(extent, workspace, this); } // Add to the cache. If it is already present, then throw an exception private void AddInternal(Map storageMap) { storageMap.DataSpace = DataSpace.CSSpace; try { base.AddInternal(storageMap); } catch (ArgumentException e) { throw new MappingException(System.Data.Entity.Strings.Mapping_Duplicate_Type(storageMap.EdmItem.Identity), e); } } // Contains whether the given StorageEntityContainerName internal bool ContainsStorageEntityContainer(string storageEntityContainerName) { ReadOnlyCollection entityContainerMaps = this.GetItems(); return entityContainerMaps.Any(map => map.StorageEntityContainer.Name.Equals(storageEntityContainerName, StringComparison.Ordinal)); } /// /// This helper method loads items based on contents of in-memory XmlReader instances. /// Assumption: This method is called only from the constructor because m_extentMappingViews is not thread safe. /// /// A list of XmlReader instances /// A list of URIs /// A list of schema errors private List LoadItems(IEnumerable xmlReaders, List mappingSchemaUris, Dictionary userDefinedQueryViewsDict, Dictionary userDefinedQueryViewsOfTypeDict, double expectedVersion) { Debug.Assert(m_memberMappings.Count == 0, "Assumption: This method is called only once, and from the constructor because m_extentMappingViews is not thread safe."); List errors = new List(); int index = -1; foreach (XmlReader xmlReader in xmlReaders) { index++; string location = null; if (mappingSchemaUris == null) { som.SchemaManager.TryGetBaseUri(xmlReader, out location); } else { location = mappingSchemaUris[index]; } StorageMappingItemLoader mapLoader = new StorageMappingItemLoader( xmlReader, this, location, // ASSUMPTION: location is only used for generating error-messages m_memberMappings); errors.AddRange(mapLoader.ParsingErrors); CheckIsSameVersion(expectedVersion, mapLoader.MappingVersion, errors); // Process container mapping. StorageEntityContainerMapping containerMapping = mapLoader.ContainerMapping; if (mapLoader.HasQueryViews && containerMapping != null) { // Compile the query views so that we can report the errors in the user specified views. CompileUserDefinedQueryViews(containerMapping, userDefinedQueryViewsDict, userDefinedQueryViewsOfTypeDict, errors); } // Add container mapping if there are no errors and entity container mapping is not already present. if (MetadataHelper.CheckIfAllErrorsAreWarnings(errors) && !this.Contains(containerMapping)) { AddInternal(containerMapping); } } CheckForDuplicateItems(EdmItemCollection, StoreItemCollection, errors); return errors; } /// /// This method compiles all the user defined query views in the . /// private static void CompileUserDefinedQueryViews(StorageEntityContainerMapping entityContainerMapping, Dictionary userDefinedQueryViewsDict, Dictionary userDefinedQueryViewsOfTypeDict, IList errors) { ConfigViewGenerator config = new ConfigViewGenerator(); foreach (StorageSetMapping setMapping in entityContainerMapping.AllSetMaps) { if (setMapping.QueryView != null) { GeneratedView generatedView; if (!userDefinedQueryViewsDict.TryGetValue(setMapping.Set, out generatedView)) { // Parse the view so that we will get back any errors in the view. if (GeneratedView.TryParseUserSpecifiedView(setMapping, setMapping.Set.ElementType, setMapping.QueryView, true, // includeSubtypes entityContainerMapping.StorageMappingItemCollection, config, /*out*/ errors, out generatedView)) { // Add first QueryView userDefinedQueryViewsDict.Add(setMapping.Set, generatedView); } // Add all type-specific QueryViews foreach (OfTypeQVCacheKey key in setMapping.GetTypeSpecificQVKeys()) { Debug.Assert(key.First.Equals(setMapping.Set)); if (GeneratedView.TryParseUserSpecifiedView(setMapping, key.Second.First, // type setMapping.GetTypeSpecificQueryView(key), key.Second.Second, // includeSubtypes entityContainerMapping.StorageMappingItemCollection, config, /*out*/ errors, out generatedView)) { userDefinedQueryViewsOfTypeDict.Add(key, generatedView); } } } } } } private void CheckIsSameVersion(double expectedVersion, double currentLoaderVersion, IList errors) { if (m_mappingVersion == XmlConstants.UndefinedVersion) { m_mappingVersion = currentLoaderVersion; } if (expectedVersion != XmlConstants.UndefinedVersion && currentLoaderVersion != XmlConstants.UndefinedVersion && currentLoaderVersion != expectedVersion) { // Check that the mapping version is the same as the storage and model version errors.Add( new EdmSchemaError( Strings.Mapping_DifferentMappingEdmStoreVersion, (int)StorageMappingErrorCode.MappingDifferentMappingEdmStoreVersion, EdmSchemaErrorSeverity.Error)); } if (currentLoaderVersion != m_mappingVersion && currentLoaderVersion != XmlConstants.UndefinedVersion) { // Check that the mapping versions are all consistent with each other errors.Add( new EdmSchemaError( Strings.CannotLoadDifferentVersionOfSchemaInTheSameItemCollection, (int)StorageMappingErrorCode.CannotLoadDifferentVersionOfSchemaInTheSameItemCollection, EdmSchemaErrorSeverity.Error)); } } /// /// Return the update view loader /// /// internal ViewLoader GetUpdateViewLoader() { if (_viewLoader == null) { _viewLoader = new ViewLoader(this); } return _viewLoader; } /// /// this method will be called in metadatworkspace, the signature is the same as the one in ViewDictionary /// /// /// /// /// /// /// internal bool TryGetGeneratedViewOfType(MetadataWorkspace workspace, EntitySetBase entity, EntityTypeBase type, bool includeSubtypes, out GeneratedView generatedView) { return this.m_viewDictionary.TryGetGeneratedViewOfType(workspace, entity, type, includeSubtypes, out generatedView); } // Check for duplicate items (items with same name) in edm item collection and store item collection. Mapping is the only logical place to do this. // The only other place is workspace, but that is at the time of registering item collections (only when the second one gets registered) and we // will have to throw exceptions at that time. If we do this check in mapping, we might throw error in a more consistent way (by adding it to error // collection). Also if someone is just creating item collection, and not registering it with workspace (tools), doing it in mapping makes more sense private static void CheckForDuplicateItems(EdmItemCollection edmItemCollection, StoreItemCollection storeItemCollection, List errorCollection) { Debug.Assert(edmItemCollection != null && storeItemCollection != null && errorCollection != null, "The parameters must not be null in CheckForDuplicateItems"); foreach (GlobalItem item in edmItemCollection) { if (storeItemCollection.Contains(item.Identity)) { errorCollection.Add(new EdmSchemaError(Strings.Mapping_ItemWithSameNameExistsBothInCSpaceAndSSpace(item.Identity), (int)StorageMappingErrorCode.ItemWithSameNameExistsBothInCSpaceAndSSpace, EdmSchemaErrorSeverity.Error)); } } } }//---- ItemCollection }//----