Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Mapping / StorageEntityContainerMapping.cs
1 //---------------------------------------------------------------------
2 // <copyright file="StorageEntityContainerMapping.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System;
10 using System.Collections.Generic;
11 using System.Collections.ObjectModel;
12 using System.Linq;
13 using System.Text;
14 using System.Xml;
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;
21
22 namespace System.Data.Mapping {
23
24     using CellGroup = Set<Cell>;
25
26     /// <summary>
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.
29     /// <example>
30     /// For Example if conceptually you could represent the CS MSL file as following
31     /// ---Mapping 
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.
38     /// </example>
39     /// <remarks>
40     /// We currently assume that an Entity Container on the C side
41     /// is mapped to a single Entity Container in the S - space.
42     /// </remarks>
43     /// </summary>
44     internal class StorageEntityContainerMapping : Map
45  {
46         #region Constructors
47         /// <summary>
48         /// Construct a new EntityContainer mapping object 
49         /// passing in the C-space EntityContainer  and
50         /// the s-space Entity container metadata objects.
51         /// </summary>
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)
55         {
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;
63         }
64         #endregion
65
66         #region Fields
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;
80
81         #endregion
82
83         #region Properties
84         public StorageMappingItemCollection StorageMappingItemCollection
85         {
86             get
87             {
88                 return m_storageMappingItemCollection;
89             }
90         }
91
92         /// <summary>
93         /// Gets the type kind for this item
94         /// </summary>
95         public override BuiltInTypeKind BuiltInTypeKind {
96             get { return BuiltInTypeKind.MetadataItem; }
97         }
98
99         /// <summary>
100         /// The Entity Container Metadata object on the C-side
101         /// for which the mapping is being represented.
102         /// </summary>
103         internal override MetadataItem EdmItem {
104             get {
105                 return this.m_entityContainer;
106             }
107         }
108
109         internal override string Identity {
110             get {
111                 return identity;
112             }
113         }
114
115         /// <summary>
116         /// Indicates whether there are no Set mappings
117         /// in the container mapping.
118         /// </summary>
119         internal bool IsEmpty
120         {
121             get
122             {
123                 return ((m_entitySetMappings.Count == 0)
124                 && (m_associationSetMappings.Count == 0));
125             }
126         }
127
128         /// <summary>
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.
131         /// </summary>
132         internal bool HasViews
133         {
134             get
135             {
136                 return HasMappingFragments()
137                     || AllSetMaps.Any((StorageSetMapping setMap) => setMap.QueryView != null);
138             }
139         }
140
141
142         internal string SourceLocation {
143             get { return m_sourceLocation; }
144             set { m_sourceLocation = value; }
145         }
146
147         /// <summary>
148         /// The Entity Container Metadata object on the C-side
149         /// for which the mapping is being represented.
150         /// </summary>
151         internal EntityContainer EdmEntityContainer {
152             get {
153                 return this.m_entityContainer;
154             }
155         }
156
157         /// <summary>
158         /// The Entity Container Metadata object on the C-side
159         /// for which the mapping is being represented.
160         /// </summary>
161         internal EntityContainer StorageEntityContainer {
162             get {
163                 return this.m_storageEntityContainer;
164             }
165         }
166
167         /// <summary>
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.
171         /// </summary>
172         internal ReadOnlyCollection<StorageSetMapping> EntitySetMaps {
173             get {
174                 return new List<StorageSetMapping>(this.m_entitySetMappings.Values).AsReadOnly();
175             }
176         }
177
178         /// <summary>
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.
184         /// </summary>
185         /// <remarks>
186         /// The reason we have RelationshipSetMaps is to be consistent with CDM metadata
187         /// which treats both associations and compositions as Relationships.
188         /// </remarks>
189         internal ReadOnlyCollection<StorageSetMapping> RelationshipSetMaps {
190             get {
191                 return new List<StorageSetMapping>(this.m_associationSetMappings.Values).AsReadOnly();
192             }
193         }
194
195         /// <summary>
196         /// a list of all the  set maps under this
197         /// container. 
198         /// </summary>
199         internal IEnumerable<StorageSetMapping> AllSetMaps
200         {
201             get
202             {
203                 return System.Linq.Enumerable.Concat(this.m_entitySetMappings.Values, this.m_associationSetMappings.Values);
204             }
205         }
206
207         /// <summary>
208         /// Line Number in MSL file where the EntityContainer Mapping Element's Start Tag is present.
209         /// </summary>
210         internal int StartLineNumber
211         {
212             get
213             {
214                 return m_startLineNumber;
215             }
216             set
217             {
218                 m_startLineNumber = value;
219             }
220         }
221
222         /// <summary>
223         /// Line Position in MSL file where the EntityContainer Mapping Element's Start Tag is present.
224         /// </summary>
225         internal int StartLinePosition
226         {
227             get
228             {
229                 return m_startLinePosition;
230             }
231             set
232             {
233                 m_startLinePosition = value;
234             }
235         }
236
237         /// <summary>
238         /// Indicates whether to validate the mapping or not.
239         /// </summary>
240         internal bool Validate
241         {
242             get
243             {
244                 return m_validate;
245             }
246         }
247
248         /// <summary>
249         /// Indicates whether to generate the update views or not.
250         /// </summary>
251         internal bool GenerateUpdateViews
252         {
253             get
254             {
255                 return m_generateUpdateViews;
256             }
257         }
258         #endregion
259
260         #region Methods
261         /// <summary>
262         /// get an EntitySet mapping based upon the name of the entity set.
263         /// </summary>
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);
270             return setMapping;
271         }
272
273         /// <summary>
274         /// Get a RelationShip set mapping based upon the name of the relationship set
275         /// </summary>
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);
282             return setMapping;
283         }
284
285         /// <summary>
286         /// Get a RelationShipSet mapping that has the passed in EntitySet as one of the ends and is mapped to the
287         /// table.
288         /// </summary>
289         internal IEnumerable<StorageAssociationSetMapping> GetRelationshipSetMappingsFor(EntitySetBase edmEntitySet, EntitySetBase storeEntitySet )
290         {
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;
296         }
297
298
299         /// <summary>
300         /// Get a set mapping based upon the name of the set
301         /// </summary>
302         /// <param name="setName"></param>
303         /// <returns></returns>
304         internal StorageSetMapping GetSetMapping(string setName)
305         {
306             StorageSetMapping setMap = GetEntitySetMapping(setName);
307             if (setMap == null)
308             {
309                 setMap = GetRelationshipSetMapping(setName);
310             }
311             return setMap;
312         }
313
314
315         /// <summary>
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.
319         /// </summary>
320         internal void AddEntitySetMapping(StorageSetMapping setMapping) {
321             if (!this.m_entitySetMappings.ContainsKey(setMapping.Set.Name))
322                 this.m_entitySetMappings.Add(setMapping.Set.Name, setMapping);
323         }
324
325         /// <summary>
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.
329         /// </summary>
330         internal void AddAssociationSetMapping(StorageSetMapping setMapping) {
331             this.m_associationSetMappings.Add(setMapping.Set.Name, setMapping);
332         }
333
334         /// <summary>
335         /// check whether the EntityContainerMapping contains
336         /// the map for the given AssociationSet
337         /// </summary>
338         /// <param name="associationSet"></param>
339         /// <returns></returns>
340         internal bool ContainsAssociationSetMapping(AssociationSet associationSet) {
341             return this.m_associationSetMappings.ContainsKey(associationSet.Name);
342         }
343
344         /// <summary>
345         /// Returns whether the Set Map for the given set has a query view or not
346         /// </summary>
347         /// <param name="setName"></param>
348         /// <returns></returns>
349         internal bool HasQueryViewForSetMap(string setName)
350         {            
351             StorageSetMapping set = GetSetMapping(setName);
352             if (set != null)
353             {
354                 return (set.QueryView != null);
355             }
356             return false;
357         }
358
359         internal bool HasMappingFragments()
360         {
361             foreach (var extentMap in this.AllSetMaps)
362             {
363                 foreach (var typeMap in extentMap.TypeMappings)
364                 {
365                     if (typeMap.MappingFragments.Count > 0)
366                     {
367                         return true;
368                     }
369                 }
370             }
371             return false;
372         }
373
374         ///<summary>
375         /// The method builds up the spaces required for pretty printing each 
376         /// part of the mapping.
377         ///</summary>
378         internal static string GetPrettyPrintString(ref int index) {
379
380             string spaces = "";
381             spaces = spaces.PadLeft(index, ' ');
382             Console.WriteLine(spaces + "|");
383             Console.WriteLine(spaces + "|");
384             index++;
385             spaces = spaces.PadLeft(index, ' ');
386             Console.Write(spaces + "-");
387             index++;
388             spaces = spaces.PadLeft(index, ' ');
389             Console.Write("-");
390             index++;
391             spaces = spaces.PadLeft(index, ' ');
392             return spaces;
393         }
394
395         /// <summary>
396         /// This method is primarily for debugging purposes.
397         /// Will be removed shortly.
398         /// </summary>
399         /// <param name="index"></param>
400         internal void Print(int index) {
401             string spaces = "";
402             StringBuilder sb = new StringBuilder();
403             sb.Append(spaces);
404             sb.Append("EntityContainerMapping");
405             sb.Append("   ");
406             sb.Append("Name:");
407             sb.Append(this.m_entityContainer.Name);
408             sb.Append("   ");
409             Console.WriteLine(sb.ToString());
410             foreach (StorageSetMapping extentMapping in m_entitySetMappings.Values) {
411                 extentMapping.Print(index + 5);
412             }
413             foreach (StorageSetMapping extentMapping in m_associationSetMappings.Values) {
414                 extentMapping.Print(index + 5);
415             }
416         }
417
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)
421         {
422             m_functionImportMappings.Add(functionImport, mapping);
423         }
424
425         internal bool TryGetFunctionImportMapping(EdmFunction functionImport, out FunctionImportMapping mapping)
426         {
427             return m_functionImportMappings.TryGetValue(functionImport, out mapping);
428         }
429
430         internal OutputFromComputeCellGroups GetCellgroups(InputForComputingCellGroups args)
431         { 
432             Debug.Assert(object.ReferenceEquals(this, args.ContainerMapping));
433             return m_memoizedCellGroupEvaluator.Evaluate(args);
434         }
435
436         private OutputFromComputeCellGroups ComputeCellGroups(InputForComputingCellGroups args)
437         {
438             OutputFromComputeCellGroups result = new OutputFromComputeCellGroups();
439             result.Success = true;
440
441             CellCreator cellCreator = new CellCreator(args.ContainerMapping);
442             result.Cells = cellCreator.GenerateCells(args.Config);
443             result.Identifiers = cellCreator.Identifiers;
444
445             if (result.Cells.Count <= 0)
446             {
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;
450                 return result;
451             }
452
453             result.ForeignKeyConstraints = ForeignConstraint.GetForeignConstraints(args.ContainerMapping.StorageEntityContainer);
454
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();
458
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();
461
462             return result;
463         }
464
465         #endregion
466     }
467
468     internal struct InputForComputingCellGroups : IEquatable<InputForComputingCellGroups>, IEqualityComparer<InputForComputingCellGroups>
469     {
470         internal readonly StorageEntityContainerMapping ContainerMapping;
471         internal readonly ConfigViewGenerator Config;
472
473         internal InputForComputingCellGroups(StorageEntityContainerMapping containerMapping, ConfigViewGenerator config)
474         {
475             this.ContainerMapping = containerMapping;
476             this.Config = config;
477         }
478
479         public bool Equals(InputForComputingCellGroups other)
480         {
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));
486         }
487
488         public bool Equals(InputForComputingCellGroups one, InputForComputingCellGroups two)
489         {
490             if (object.ReferenceEquals(one, two))
491             {
492                 return true;
493             }
494             if (object.ReferenceEquals(one, null) || object.ReferenceEquals(two, null))
495             {
496                 return false;
497             }
498
499             return one.Equals(two);
500         }
501
502         public int GetHashCode(InputForComputingCellGroups value)
503         {
504             if (value == null)
505             {
506                 return 0;
507             }
508
509             return value.GetHashCode();
510         }
511
512         public override int GetHashCode()
513         {
514             return this.ContainerMapping.GetHashCode();
515         }
516
517         public override bool Equals(object obj)
518         {
519             if (obj is InputForComputingCellGroups)
520             {
521                 return Equals((InputForComputingCellGroups)obj);
522             }
523             else
524             {
525                 return false;
526             }
527         }
528
529         public static bool operator ==(InputForComputingCellGroups input1, InputForComputingCellGroups input2)
530         {
531             if (object.ReferenceEquals(input1, input2))
532             {
533                 return true;
534             }
535             return input1.Equals(input2);
536         }
537
538         public static bool operator !=(InputForComputingCellGroups input1, InputForComputingCellGroups input2)
539         {
540             return !(input1 == input2);
541         }
542
543     }
544
545     internal struct OutputFromComputeCellGroups
546     {
547         internal List<Cell> Cells;
548         internal CqlIdentifiers Identifiers;
549         internal List<CellGroup> CellGroups;
550         internal List<ForeignConstraint> ForeignKeyConstraints;
551         internal bool Success;
552     }
553 }