1 //---------------------------------------------------------------------
2 // <copyright file="StorageEntityContainerMapping.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System.Collections.Generic;
11 using System.Collections.ObjectModel;
15 using System.Data.Metadata.Edm;
16 using System.Diagnostics;
17 using System.Data.Mapping.ViewGeneration;
18 using System.Data.Common.Utils;
19 using System.Data.Mapping.ViewGeneration.Structures;
20 using System.Data.Mapping.ViewGeneration.Validation;
22 namespace System.Data.Mapping {
24 using CellGroup = Set<Cell>;
27 /// Represents the Mapping metadata for the EntityContainer map in CS space.
28 /// Only one EntityContainerMapping element is allowed in the MSL file for CS mapping.
30 /// For Example if conceptually you could represent the CS MSL file as following
32 /// --EntityContainerMapping ( CNorthwind-->SNorthwind )
33 /// --EntitySetMapping
34 /// --AssociationSetMapping
35 /// The type represents the metadata for EntityContainerMapping element in the above example.
36 /// The SetMapping elements that are children of the EntityContainerMapping element
37 /// can be accessed through the properties on this type.
40 /// We currently assume that an Entity Container on the C side
41 /// is mapped to a single Entity Container in the S - space.
44 internal class StorageEntityContainerMapping : Map
48 /// Construct a new EntityContainer mapping object
49 /// passing in the C-space EntityContainer and
50 /// the s-space Entity container metadata objects.
52 /// <param name="entityContainer">Entity Continer type that is being mapped on the C-side</param>
53 /// <param name="storageEntityContainer">Entity Continer type that is being mapped on the S-side</param>
54 internal StorageEntityContainerMapping(EntityContainer entityContainer, EntityContainer storageEntityContainer, StorageMappingItemCollection storageMappingItemCollection, bool validate, bool generateUpdateViews)
56 this.m_entityContainer = entityContainer;
57 this.m_storageEntityContainer = storageEntityContainer;
58 this.m_storageMappingItemCollection = storageMappingItemCollection;
59 this.m_memoizedCellGroupEvaluator = new Memoizer<InputForComputingCellGroups, OutputFromComputeCellGroups>(ComputeCellGroups, new InputForComputingCellGroups());
60 this.identity = entityContainer.Identity;
61 this.m_validate = validate;
62 this.m_generateUpdateViews = generateUpdateViews;
67 private string identity;
68 private bool m_validate;
69 private bool m_generateUpdateViews;
70 private EntityContainer m_entityContainer; //Entity Continer type that is being mapped on the C-side
71 private EntityContainer m_storageEntityContainer; //Entity Continer type that the C-space container is being mapped to
72 private Dictionary<string, StorageSetMapping> m_entitySetMappings = new Dictionary<string, StorageSetMapping>(StringComparer.Ordinal); //A collection of EntitySetMappings under this EntityContainer mapping
73 private Dictionary<string, StorageSetMapping> m_associationSetMappings = new Dictionary<string, StorageSetMapping>(StringComparer.Ordinal); //A collection of AssociationSetMappings under this EntityContainer mapping
74 private Dictionary<EdmFunction, FunctionImportMapping> m_functionImportMappings = new Dictionary<EdmFunction, FunctionImportMapping>();
75 private string m_sourceLocation; //Schema URI for the mapping
76 private int m_startLineNumber; //Line Number for EntityContainer Mapping element start tag
77 private int m_startLinePosition; //Line position for EntityContainer Mapping element start tag
78 private readonly StorageMappingItemCollection m_storageMappingItemCollection;
79 private readonly Memoizer<InputForComputingCellGroups, OutputFromComputeCellGroups> m_memoizedCellGroupEvaluator;
84 public StorageMappingItemCollection StorageMappingItemCollection
88 return m_storageMappingItemCollection;
93 /// Gets the type kind for this item
95 public override BuiltInTypeKind BuiltInTypeKind {
96 get { return BuiltInTypeKind.MetadataItem; }
100 /// The Entity Container Metadata object on the C-side
101 /// for which the mapping is being represented.
103 internal override MetadataItem EdmItem {
105 return this.m_entityContainer;
109 internal override string Identity {
116 /// Indicates whether there are no Set mappings
117 /// in the container mapping.
119 internal bool IsEmpty
123 return ((m_entitySetMappings.Count == 0)
124 && (m_associationSetMappings.Count == 0));
129 /// Determine whether the container includes any views.
130 /// Returns true if there is at least one query or update view specified by the mapping.
132 internal bool HasViews
136 return HasMappingFragments()
137 || AllSetMaps.Any((StorageSetMapping setMap) => setMap.QueryView != null);
142 internal string SourceLocation {
143 get { return m_sourceLocation; }
144 set { m_sourceLocation = value; }
148 /// The Entity Container Metadata object on the C-side
149 /// for which the mapping is being represented.
151 internal EntityContainer EdmEntityContainer {
153 return this.m_entityContainer;
158 /// The Entity Container Metadata object on the C-side
159 /// for which the mapping is being represented.
161 internal EntityContainer StorageEntityContainer {
163 return this.m_storageEntityContainer;
168 /// a list of all the entity set maps under this
169 /// container. In CS mapping, the mapping is done
170 /// at the extent level as opposed to the type level.
172 internal ReadOnlyCollection<StorageSetMapping> EntitySetMaps {
174 return new List<StorageSetMapping>(this.m_entitySetMappings.Values).AsReadOnly();
179 /// a list of all the entity set maps under this
180 /// container. In CS mapping, the mapping is done
181 /// at the extent level as opposed to the type level.
182 /// RelationshipSetMaps will be CompositionSetMaps and
183 /// AssociationSetMaps put together.
186 /// The reason we have RelationshipSetMaps is to be consistent with CDM metadata
187 /// which treats both associations and compositions as Relationships.
189 internal ReadOnlyCollection<StorageSetMapping> RelationshipSetMaps {
191 return new List<StorageSetMapping>(this.m_associationSetMappings.Values).AsReadOnly();
196 /// a list of all the set maps under this
199 internal IEnumerable<StorageSetMapping> AllSetMaps
203 return System.Linq.Enumerable.Concat(this.m_entitySetMappings.Values, this.m_associationSetMappings.Values);
208 /// Line Number in MSL file where the EntityContainer Mapping Element's Start Tag is present.
210 internal int StartLineNumber
214 return m_startLineNumber;
218 m_startLineNumber = value;
223 /// Line Position in MSL file where the EntityContainer Mapping Element's Start Tag is present.
225 internal int StartLinePosition
229 return m_startLinePosition;
233 m_startLinePosition = value;
238 /// Indicates whether to validate the mapping or not.
240 internal bool Validate
249 /// Indicates whether to generate the update views or not.
251 internal bool GenerateUpdateViews
255 return m_generateUpdateViews;
262 /// get an EntitySet mapping based upon the name of the entity set.
264 /// /// <param name="entitySetName">the name of the entity set</param>
265 internal StorageSetMapping GetEntitySetMapping(String entitySetName) {
266 EntityUtil.CheckArgumentNull(entitySetName, "entitySetName");
267 //Key for EntitySetMapping should be EntitySet name and Entoty type name
268 StorageSetMapping setMapping = null;
269 m_entitySetMappings.TryGetValue(entitySetName, out setMapping);
274 /// Get a RelationShip set mapping based upon the name of the relationship set
276 /// <param name="relationshipSetName">the name of the relationship set</param>
277 /// <returns>the mapping for the entity set if it exists, null if it does not exist</returns>
278 internal StorageSetMapping GetRelationshipSetMapping(string relationshipSetName) {
279 EntityUtil.CheckArgumentNull(relationshipSetName, "relationshipSetName");
280 StorageSetMapping setMapping = null;
281 m_associationSetMappings.TryGetValue(relationshipSetName, out setMapping);
286 /// Get a RelationShipSet mapping that has the passed in EntitySet as one of the ends and is mapped to the
289 internal IEnumerable<StorageAssociationSetMapping> GetRelationshipSetMappingsFor(EntitySetBase edmEntitySet, EntitySetBase storeEntitySet )
291 //First select the association set maps that are mapped to this table
292 IEnumerable<StorageAssociationSetMapping> associationSetMappings = m_associationSetMappings.Values.Cast<StorageAssociationSetMapping>().Where(w => ((w.StoreEntitySet != null) && (w.StoreEntitySet == storeEntitySet)));
293 //From this again filter the ones that have the specified EntitySet on atleast one end
294 associationSetMappings = associationSetMappings.Where(associationSetMap => ((associationSetMap.Set as AssociationSet).AssociationSetEnds.Any(associationSetEnd => associationSetEnd.EntitySet == edmEntitySet)));
295 return associationSetMappings;
300 /// Get a set mapping based upon the name of the set
302 /// <param name="setName"></param>
303 /// <returns></returns>
304 internal StorageSetMapping GetSetMapping(string setName)
306 StorageSetMapping setMap = GetEntitySetMapping(setName);
309 setMap = GetRelationshipSetMapping(setName);
316 /// Adds an entity set mapping to the list of EntitySetMaps
317 /// under this entity container mapping. The method will be called
318 /// by the Mapping loader.
320 internal void AddEntitySetMapping(StorageSetMapping setMapping) {
321 if (!this.m_entitySetMappings.ContainsKey(setMapping.Set.Name))
322 this.m_entitySetMappings.Add(setMapping.Set.Name, setMapping);
326 /// Adds a association set mapping to the list of AssociationSetMaps
327 /// under this entity container mapping. The method will be called
328 /// by the Mapping loader.
330 internal void AddAssociationSetMapping(StorageSetMapping setMapping) {
331 this.m_associationSetMappings.Add(setMapping.Set.Name, setMapping);
335 /// check whether the EntityContainerMapping contains
336 /// the map for the given AssociationSet
338 /// <param name="associationSet"></param>
339 /// <returns></returns>
340 internal bool ContainsAssociationSetMapping(AssociationSet associationSet) {
341 return this.m_associationSetMappings.ContainsKey(associationSet.Name);
345 /// Returns whether the Set Map for the given set has a query view or not
347 /// <param name="setName"></param>
348 /// <returns></returns>
349 internal bool HasQueryViewForSetMap(string setName)
351 StorageSetMapping set = GetSetMapping(setName);
354 return (set.QueryView != null);
359 internal bool HasMappingFragments()
361 foreach (var extentMap in this.AllSetMaps)
363 foreach (var typeMap in extentMap.TypeMappings)
365 if (typeMap.MappingFragments.Count > 0)
375 /// The method builds up the spaces required for pretty printing each
376 /// part of the mapping.
378 internal static string GetPrettyPrintString(ref int index) {
381 spaces = spaces.PadLeft(index, ' ');
382 Console.WriteLine(spaces + "|");
383 Console.WriteLine(spaces + "|");
385 spaces = spaces.PadLeft(index, ' ');
386 Console.Write(spaces + "-");
388 spaces = spaces.PadLeft(index, ' ');
391 spaces = spaces.PadLeft(index, ' ');
396 /// This method is primarily for debugging purposes.
397 /// Will be removed shortly.
399 /// <param name="index"></param>
400 internal void Print(int index) {
402 StringBuilder sb = new StringBuilder();
404 sb.Append("EntityContainerMapping");
407 sb.Append(this.m_entityContainer.Name);
409 Console.WriteLine(sb.ToString());
410 foreach (StorageSetMapping extentMapping in m_entitySetMappings.Values) {
411 extentMapping.Print(index + 5);
413 foreach (StorageSetMapping extentMapping in m_associationSetMappings.Values) {
414 extentMapping.Print(index + 5);
418 // Methods to modify and access function imports, which association a "functionImport" declared
419 // in the model entity container with a targetFunction declared in the target
420 internal void AddFunctionImportMapping(EdmFunction functionImport, FunctionImportMapping mapping)
422 m_functionImportMappings.Add(functionImport, mapping);
425 internal bool TryGetFunctionImportMapping(EdmFunction functionImport, out FunctionImportMapping mapping)
427 return m_functionImportMappings.TryGetValue(functionImport, out mapping);
430 internal OutputFromComputeCellGroups GetCellgroups(InputForComputingCellGroups args)
432 Debug.Assert(object.ReferenceEquals(this, args.ContainerMapping));
433 return m_memoizedCellGroupEvaluator.Evaluate(args);
436 private OutputFromComputeCellGroups ComputeCellGroups(InputForComputingCellGroups args)
438 OutputFromComputeCellGroups result = new OutputFromComputeCellGroups();
439 result.Success = true;
441 CellCreator cellCreator = new CellCreator(args.ContainerMapping);
442 result.Cells = cellCreator.GenerateCells(args.Config);
443 result.Identifiers = cellCreator.Identifiers;
445 if (result.Cells.Count <= 0)
447 //When type-specific QVs are asked for but not defined in the MSL we should return without generating
448 // Query pipeline will handle this appropriately by asking for UNION ALL view.
449 result.Success = false;
453 result.ForeignKeyConstraints = ForeignConstraint.GetForeignConstraints(args.ContainerMapping.StorageEntityContainer);
455 // Go through each table and determine their foreign key constraints
456 CellPartitioner partitioner = new CellPartitioner(result.Cells, result.ForeignKeyConstraints);
457 List<CellGroup> cellGroups = partitioner.GroupRelatedCells();
459 //Clone cell groups- i.e, List<Set<Cell>> - upto cell before storing it in the cache because viewgen modified the Cell structure
460 result.CellGroups = cellGroups.Select(setOfcells => new CellGroup(setOfcells.Select(cell => new Cell(cell)))).ToList();
468 internal struct InputForComputingCellGroups : IEquatable<InputForComputingCellGroups>, IEqualityComparer<InputForComputingCellGroups>
470 internal readonly StorageEntityContainerMapping ContainerMapping;
471 internal readonly ConfigViewGenerator Config;
473 internal InputForComputingCellGroups(StorageEntityContainerMapping containerMapping, ConfigViewGenerator config)
475 this.ContainerMapping = containerMapping;
476 this.Config = config;
479 public bool Equals(InputForComputingCellGroups other)
481 // Isn't this funny? We are not using Memoizer for function memoization. Args Entity and Config don't matter!
482 // If I were to compare Entity this would not use the cache for cases when I supply different entity set. However,
483 // the cell groups belong to ALL entity sets.
484 return (this.ContainerMapping.Equals(other.ContainerMapping)
485 && this.Config.Equals(other.Config));
488 public bool Equals(InputForComputingCellGroups one, InputForComputingCellGroups two)
490 if (object.ReferenceEquals(one, two))
494 if (object.ReferenceEquals(one, null) || object.ReferenceEquals(two, null))
499 return one.Equals(two);
502 public int GetHashCode(InputForComputingCellGroups value)
509 return value.GetHashCode();
512 public override int GetHashCode()
514 return this.ContainerMapping.GetHashCode();
517 public override bool Equals(object obj)
519 if (obj is InputForComputingCellGroups)
521 return Equals((InputForComputingCellGroups)obj);
529 public static bool operator ==(InputForComputingCellGroups input1, InputForComputingCellGroups input2)
531 if (object.ReferenceEquals(input1, input2))
535 return input1.Equals(input2);
538 public static bool operator !=(InputForComputingCellGroups input1, InputForComputingCellGroups input2)
540 return !(input1 == input2);
545 internal struct OutputFromComputeCellGroups
547 internal List<Cell> Cells;
548 internal CqlIdentifiers Identifiers;
549 internal List<CellGroup> CellGroups;
550 internal List<ForeignConstraint> ForeignKeyConstraints;
551 internal bool Success;