Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity.Design / System / Data / Entity / Design / Common / OneToOneMappingSerializer.cs
blobe5b6fccbfdbd18012a8585668b59d6b6cebc8856
1 //---------------------------------------------------------------------
2 // <copyright file="OneToOneMappingSerializer.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System.Collections.Generic;
10 using System.Data.Common;
11 using System.Data.Common.Utils;
12 using System.Data.Mapping;
13 using System.Data.Metadata.Edm;
14 using System.Diagnostics;
15 using System.Linq;
16 using System.Xml;
18 namespace System.Data.Entity.Design.Common
20 internal class OneToOneMappingSerializer
22 internal class MappingLookups
24 internal Dictionary<EntityType, EntityType> StoreEntityTypeToModelEntityType = new Dictionary<EntityType, EntityType>();
25 internal Dictionary<EdmProperty, EdmProperty> StoreEdmPropertyToModelEdmProperty = new Dictionary<EdmProperty, EdmProperty>();
26 internal Dictionary<EntitySet, EntitySet> StoreEntitySetToModelEntitySet = new Dictionary<EntitySet, EntitySet>();
28 internal Dictionary<AssociationType, AssociationType> StoreAssociationTypeToModelAssociationType = new Dictionary<AssociationType, AssociationType>();
29 internal Dictionary<AssociationEndMember, AssociationEndMember> StoreAssociationEndMemberToModelAssociationEndMember = new Dictionary<AssociationEndMember, AssociationEndMember>();
30 internal Dictionary<AssociationSet, AssociationSet> StoreAssociationSetToModelAssociationSet = new Dictionary<AssociationSet, AssociationSet>();
31 internal Dictionary<AssociationSetEnd, AssociationSetEnd> StoreAssociationSetEndToModelAssociationSetEnd = new Dictionary<AssociationSetEnd, AssociationSetEnd>();
33 internal List<CollapsedEntityAssociationSet> CollapsedEntityAssociationSets = new List<CollapsedEntityAssociationSet>();
35 internal List<Tuple<EdmFunction, EdmFunction>> StoreFunctionToFunctionImport = new List<Tuple<EdmFunction, EdmFunction>>();
38 // this class represents a construct found in the ssdl where a link table
39 // contained no data (all its properties were part of its keys)
40 // it has exactly two associations
41 // the entity type is the TO side of both associations
42 // all the colums are used as TO columns in the constraint
43 internal class CollapsedEntityAssociationSet
45 private EntitySet _storeEntitySet;
46 private List<AssociationSet> _storeAssociationSets = new List<AssociationSet>(2);
47 private AssociationSet _modelAssociationSet;
49 public AssociationSet ModelAssociationSet
51 get { return _modelAssociationSet; }
52 set
54 Debug.Assert(_modelAssociationSet == null, "why is this getting set multiple times, it should only be set after the new set is created");
55 _modelAssociationSet = value;
59 public CollapsedEntityAssociationSet(EntitySet entitySet)
61 Debug.Assert(entitySet != null, "entitySet parameter is null");
62 _storeEntitySet = entitySet;
65 public EntitySet EntitySet
67 get { return _storeEntitySet; }
70 public List<AssociationSet> AssociationSets
72 get { return _storeAssociationSets; }
75 public void GetStoreAssociationSetEnd(int index, out AssociationSetEnd storeAssociationSetEnd, out RelationshipMultiplicity multiplicity, out OperationAction deleteBehavior)
77 Debug.Assert(index >= 0 && index < AssociationSets.Count, "out of bounds dude!!");
78 Debug.Assert(AssociationSets.Count == 2, "This code depends on only having exactly two AssociationSets");
79 GetFromAssociationSetEnd(AssociationSets[index], AssociationSets[(index+1)%2], out storeAssociationSetEnd, out multiplicity, out deleteBehavior);
82 private void GetFromAssociationSetEnd(AssociationSet definingSet, AssociationSet multiplicitySet, out AssociationSetEnd associationSetEnd, out RelationshipMultiplicity multiplicity, out OperationAction deleteBehavior)
84 // for a situation like this (CD is CascadeDelete)
85 //
86 // -------- CD -------- CD --------
87 // | A |1 <- 1| AtoB |* <- 1| B |
88 // | |-------| |-------| |
89 // | | | | | |
90 // -------- -------- --------
91 //
92 // You get
93 // -------- CD --------
94 // | A |* <- 1| B |
95 // | |-------| |
96 // | | | |
97 // -------- --------
98 //
99 // Notice that the of the new "link table association" muliplicities are opposite of what comming into the original link table
100 // this seems counter intuitive at first, but makes sense when you think all the way through it
102 // CascadeDelete Behavior (we can assume the runtime will always delete cascade
103 // to the link table from the outside tables (it actually doesn't, but that is a bug))
104 // Store Effective
105 // A -> AToB <- B None
106 // A <- AToB <- B <-
107 // A -> AToB -> B ->
108 // A <- AToB -> B None
109 // A <- AToB B <-
110 // A AToB -> B ->
111 // A -> AToB B None
112 // A AToB <- B None
114 // Other CascadeDelete rules
115 // 1. Can't have a delete from a Many multiplicity end
116 // 2. Can't have a delete on both ends
119 associationSetEnd = GetAssociationSetEnd(definingSet, true);
120 AssociationSetEnd multiplicityAssociationSetEnd = GetAssociationSetEnd(multiplicitySet, false);
121 multiplicity = multiplicityAssociationSetEnd.CorrespondingAssociationEndMember.RelationshipMultiplicity;
122 deleteBehavior = OperationAction.None;
123 if (multiplicity != RelationshipMultiplicity.Many)
125 OperationAction otherEndBehavior = GetAssociationSetEnd(definingSet, false).CorrespondingAssociationEndMember.DeleteBehavior;
126 if(otherEndBehavior == OperationAction.None)
128 // Since the other end does not have an operation
129 // that means that only one end could possibly have an operation, that is good
130 // so set it the operation
131 deleteBehavior = multiplicityAssociationSetEnd.CorrespondingAssociationEndMember.DeleteBehavior;
136 private static AssociationSetEnd GetAssociationSetEnd(AssociationSet set, bool fromEnd)
138 Debug.Assert(set.ElementType.ReferentialConstraints.Count == 1, "no referenctial constraint for association[0]");
139 ReferentialConstraint constraint = set.ElementType.ReferentialConstraints[0];
141 Debug.Assert(set.AssociationSetEnds.Count == 2, "Associations are assumed to have two ends");
142 int toEndIndex, fromEndIndex;
143 if (set.AssociationSetEnds[0].CorrespondingAssociationEndMember == constraint.FromRole)
145 fromEndIndex = 0;
146 toEndIndex = 1;
149 else
151 fromEndIndex = 1;
152 toEndIndex = 0;
156 if (fromEnd)
158 return set.AssociationSetEnds[fromEndIndex];
160 else
162 return set.AssociationSetEnds[toEndIndex];
166 public bool MeetsRequirementsForCollapsableAssociation
170 if (_storeAssociationSets.Count != 2)
171 return false;
173 ReferentialConstraint constraint0;
174 ReferentialConstraint constraint1;
175 GetConstraints(out constraint0, out constraint1);
176 if (!IsEntityDependentSideOfBothAssociations(constraint0, constraint1))
177 return false;
179 if (!IsAtLeastOneColumnOfBothDependentRelationshipColumnSetsNonNullable(constraint0, constraint1))
180 return false;
182 if (!AreAllEntityColumnsMappedAsToColumns(constraint0, constraint1))
183 return false;
185 if (IsAtLeastOneColumnFKInBothAssociations(constraint0, constraint1))
186 return false;
188 return true;
192 private bool IsAtLeastOneColumnFKInBothAssociations(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
194 return constraint1.ToProperties.Any(c => constraint0.ToProperties.Contains(c));
197 private bool IsAtLeastOneColumnOfBothDependentRelationshipColumnSetsNonNullable(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
199 return ToPropertyHasNonNullableColumn(constraint0) && ToPropertyHasNonNullableColumn(constraint1);
202 private static bool ToPropertyHasNonNullableColumn(ReferentialConstraint constraint)
204 foreach (EdmProperty property in constraint.ToProperties)
206 if (!property.Nullable)
208 return true;
211 return false;
214 private bool AreAllEntityColumnsMappedAsToColumns(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
216 Set<string> names = new Set<string>();
217 AddToPropertyNames(constraint0, names);
218 AddToPropertyNames(constraint1, names);
219 return names.Count == _storeEntitySet.ElementType.Properties.Count;
222 private static void AddToPropertyNames(ReferentialConstraint constraint, Set<string> names)
224 foreach (EdmProperty property in constraint.ToProperties)
226 names.Add(property.Name);
230 private bool IsEntityDependentSideOfBothAssociations(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
232 return ((RefType)constraint0.ToRole.TypeUsage.EdmType).ElementType == _storeEntitySet.ElementType && ((RefType)constraint1.ToRole.TypeUsage.EdmType).ElementType == _storeEntitySet.ElementType;
235 private void GetConstraints(out ReferentialConstraint constraint0, out ReferentialConstraint constraint1)
237 Debug.Assert(_storeAssociationSets.Count == 2, "don't call this method if you don't have two associations");
238 Debug.Assert(_storeAssociationSets[0].ElementType.ReferentialConstraints.Count == 1, "no referenctial constraint for association[0]");
239 Debug.Assert(_storeAssociationSets[1].ElementType.ReferentialConstraints.Count == 1, "no referenctial constraint for association[1]");
240 constraint0 = _storeAssociationSets[0].ElementType.ReferentialConstraints[0];
241 constraint1 = _storeAssociationSets[1].ElementType.ReferentialConstraints[0];
245 private MappingLookups _lookups;
246 private EntityContainer _storeContainer;
247 private EntityContainer _modelContainer;
248 private string _xmlNamespace;
250 internal OneToOneMappingSerializer(MappingLookups lookups,
251 EntityContainer storeContainer,
252 EntityContainer modelContainer,
253 Version schemaVersion)
255 EDesignUtil.CheckArgumentNull(lookups, "lookups");
256 EDesignUtil.CheckArgumentNull(storeContainer, "storeContainer");
257 EDesignUtil.CheckArgumentNull(modelContainer, "modelContainer");
258 _lookups = lookups;
259 _storeContainer = storeContainer;
260 _modelContainer = modelContainer;
261 _xmlNamespace = EntityFrameworkVersions.GetSchemaNamespace(schemaVersion, DataSpace.CSSpace);
264 public void WriteXml(XmlWriter writer)
266 EDesignUtil.CheckArgumentNull(writer, "writer");
268 WriteMappingStartElement(writer);
269 WriteEntityContainerMappingElement(writer);
270 writer.WriteEndElement();
273 private void WriteEntityContainerMappingElement(XmlWriter writer)
275 writer.WriteStartElement(StorageMslConstructs.EntityContainerMappingElement, _xmlNamespace);
276 writer.WriteAttributeString(StorageMslConstructs.StorageEntityContainerAttribute, _storeContainer.Name);
277 writer.WriteAttributeString(StorageMslConstructs.CdmEntityContainerAttribute, _modelContainer.Name);
279 foreach (EntitySet set in _lookups.StoreEntitySetToModelEntitySet.Keys)
281 EntitySet modelEntitySet = _lookups.StoreEntitySetToModelEntitySet[set];
282 WriteEntitySetMappingElement(writer, set, modelEntitySet);
285 foreach(AssociationSet set in _lookups.StoreAssociationSetToModelAssociationSet.Keys)
287 AssociationSet modelAssociationSet = _lookups.StoreAssociationSetToModelAssociationSet[set];
288 WriteAssociationSetMappingElement(writer, set, modelAssociationSet);
291 foreach (CollapsedEntityAssociationSet set in _lookups.CollapsedEntityAssociationSets)
293 WriteAssociationSetMappingElement(writer, set);
296 foreach (var functionMapping in _lookups.StoreFunctionToFunctionImport)
298 var storeFunction = functionMapping.Item1;
299 var functionImport = functionMapping.Item2;
300 WriteFunctionImportMappingElement(writer, storeFunction, functionImport);
303 writer.WriteEndElement();
306 private void WriteFunctionImportMappingElement(XmlWriter writer, EdmFunction storeFunction, EdmFunction functionImport)
308 Debug.Assert(storeFunction.IsComposableAttribute, "storeFunction.IsComposableAttribute");
309 Debug.Assert(storeFunction.ReturnParameters.Count == 1, "storeFunction.ReturnParameters.Count == 1");
310 Debug.Assert(functionImport.IsComposableAttribute, "functionImport.IsComposableAttribute");
311 Debug.Assert(functionImport.ReturnParameters.Count == 1, "functionImport.ReturnParameters.Count == 1");
313 writer.WriteStartElement(StorageMslConstructs.FunctionImportMappingElement, _xmlNamespace);
314 writer.WriteAttributeString(StorageMslConstructs.FunctionImportMappingFunctionNameAttribute, storeFunction.FullName);
315 writer.WriteAttributeString(StorageMslConstructs.FunctionImportMappingFunctionImportNameAttribute, functionImport.Name);
317 RowType tvfReturnType = TypeHelpers.GetTvfReturnType(storeFunction);
318 if (tvfReturnType != null)
320 // Table-valued function
321 Debug.Assert(functionImport.ReturnParameter.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType, "functionImport is expected to return Collection(ComplexType)");
322 var modelCollectionType = (CollectionType)functionImport.ReturnParameter.TypeUsage.EdmType;
323 Debug.Assert(modelCollectionType.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType, "functionImport is expected to return Collection(ComplexType)");
324 var modelComplexType = (ComplexType)modelCollectionType.TypeUsage.EdmType;
326 // Write ResultMapping/ComplexTypeMapping
327 writer.WriteStartElement(StorageMslConstructs.FunctionImportMappingResultMapping, _xmlNamespace);
328 writer.WriteStartElement(StorageMslConstructs.ComplexTypeMappingElement, _xmlNamespace);
329 writer.WriteAttributeString(StorageMslConstructs.ComplexTypeMappingTypeNameAttribute, modelComplexType.FullName);
330 foreach (EdmProperty storeProperty in tvfReturnType.Properties)
332 EdmProperty modelProperty = _lookups.StoreEdmPropertyToModelEdmProperty[storeProperty];
333 WriteScalarPropertyElement(writer, storeProperty, modelProperty);
335 writer.WriteEndElement();
336 writer.WriteEndElement();
338 else
340 Debug.Fail("Only TVF store functions are supported.");
343 writer.WriteEndElement();
346 private void WriteAssociationSetMappingElement(XmlWriter writer, CollapsedEntityAssociationSet collapsedAssociationSet)
348 if (!collapsedAssociationSet.ModelAssociationSet.ElementType.IsForeignKey)
350 writer.WriteStartElement(StorageMslConstructs.AssociationSetMappingElement, _xmlNamespace);
351 writer.WriteAttributeString(StorageMslConstructs.AssociationSetMappingNameAttribute, collapsedAssociationSet.ModelAssociationSet.Name);
352 writer.WriteAttributeString(StorageMslConstructs.AssociationSetMappingTypeNameAttribute, collapsedAssociationSet.ModelAssociationSet.ElementType.FullName);
353 writer.WriteAttributeString(StorageMslConstructs.AssociationSetMappingStoreEntitySetAttribute, collapsedAssociationSet.EntitySet.Name);
356 for (int i = 0; i < collapsedAssociationSet.AssociationSets.Count; i++)
358 AssociationSetEnd storeEnd;
359 RelationshipMultiplicity multiplicity;
360 OperationAction deleteBehavior;
361 collapsedAssociationSet.GetStoreAssociationSetEnd(i, out storeEnd, out multiplicity, out deleteBehavior);
362 AssociationSetEnd modelEnd = _lookups.StoreAssociationSetEndToModelAssociationSetEnd[storeEnd];
363 WriteEndPropertyElement(writer, storeEnd, modelEnd);
366 // don't need condition element
368 writer.WriteEndElement();
372 private void WriteAssociationSetMappingElement(XmlWriter writer, AssociationSet store, AssociationSet model)
374 if (!model.ElementType.IsForeignKey)
376 writer.WriteStartElement(StorageMslConstructs.AssociationSetMappingElement, _xmlNamespace);
377 writer.WriteAttributeString(StorageMslConstructs.AssociationSetMappingNameAttribute, model.Name);
378 writer.WriteAttributeString(StorageMslConstructs.AssociationSetMappingTypeNameAttribute, model.ElementType.FullName);
380 // all column names must be the primary key of the
381 // end, but as columns in the Fk table.
382 AssociationSetEnd foreignKeyTableEnd = GetAssociationSetEndForForeignKeyTable(store);
383 writer.WriteAttributeString(StorageMslConstructs.AssociationSetMappingStoreEntitySetAttribute, foreignKeyTableEnd.EntitySet.Name);
385 foreach (AssociationSetEnd storeEnd in store.AssociationSetEnds)
387 AssociationSetEnd modelEnd = _lookups.StoreAssociationSetEndToModelAssociationSetEnd[storeEnd];
388 WriteEndPropertyElement(writer, storeEnd, modelEnd);
391 ReferentialConstraint constraint = GetReferentialConstraint(store);
392 foreach (EdmProperty fkColumn in constraint.ToProperties)
394 if (fkColumn.Nullable)
396 WriteConditionElement(writer, fkColumn);
400 writer.WriteEndElement();
404 private void WriteConditionElement(XmlWriter writer, EdmProperty fkColumn)
406 writer.WriteStartElement(StorageMslConstructs.ConditionElement, _xmlNamespace);
407 writer.WriteAttributeString(StorageMslConstructs.ConditionColumnNameAttribute, fkColumn.Name);
408 writer.WriteAttributeString(StorageMslConstructs.ConditionIsNullAttribute, "false");
409 writer.WriteEndElement();
412 private static AssociationSetEnd GetAssociationSetEndForForeignKeyTable(AssociationSet store)
414 ReferentialConstraint constraint = GetReferentialConstraint(store);
415 return store.AssociationSetEnds.GetValue(constraint.ToRole.Name, false);
418 internal static ReferentialConstraint GetReferentialConstraint(AssociationSet set)
420 // this seeems like a hack, but it is what we have right now.
421 ReferentialConstraint constraint = null;
422 foreach (ReferentialConstraint rc in set.ElementType.ReferentialConstraints)
424 Debug.Assert(constraint == null, "we should only get one");
425 constraint = rc;
427 Debug.Assert(constraint != null, "we should get at least one constraint");
428 return constraint;
431 private void WriteEndPropertyElement(XmlWriter writer, AssociationSetEnd store, AssociationSetEnd model)
433 writer.WriteStartElement(StorageMslConstructs.EndPropertyMappingElement, _xmlNamespace);
434 writer.WriteAttributeString(StorageMslConstructs.EndPropertyMappingNameAttribute, model.Name);
435 foreach (EdmProperty storeKeyMember in store.EntitySet.ElementType.KeyMembers)
437 EdmProperty modelKeyMember = _lookups.StoreEdmPropertyToModelEdmProperty[storeKeyMember];
438 EdmProperty storeFkTableMember = GetAssociatedFkColumn(store, storeKeyMember);
439 WriteScalarPropertyElement(writer, storeFkTableMember, modelKeyMember);
441 writer.WriteEndElement();
444 private static EdmProperty GetAssociatedFkColumn(AssociationSetEnd store, EdmProperty storeKeyProperty)
446 ReferentialConstraint constraint = GetReferentialConstraint(store.ParentAssociationSet);
447 if (store.Name == constraint.FromRole.Name)
449 for (int i = 0; i < constraint.FromProperties.Count; i++)
451 if (constraint.FromProperties[i] == storeKeyProperty)
453 // return the matching Fk column
454 return constraint.ToProperties[i];
459 return storeKeyProperty;
462 private void WriteEntitySetMappingElement(XmlWriter writer, EntitySet store, EntitySet model)
464 writer.WriteStartElement(StorageMslConstructs.EntitySetMappingElement, _xmlNamespace);
465 writer.WriteAttributeString(StorageMslConstructs.EntitySetMappingNameAttribute, model.Name);
466 WriteEntityTypeMappingElement(writer, store, model);
467 writer.WriteEndElement();
470 private void WriteEntityTypeMappingElement(XmlWriter writer, EntitySet store, EntitySet model)
472 writer.WriteStartElement(StorageMslConstructs.EntityTypeMappingElement, _xmlNamespace);
473 writer.WriteAttributeString(StorageMslConstructs.EntityTypeMappingTypeNameAttribute, model.ElementType.FullName);
474 WriteMappingFragmentElement(writer, store, model);
475 writer.WriteEndElement();
478 private void WriteMappingFragmentElement(XmlWriter writer, EntitySet store, EntitySet model)
480 writer.WriteStartElement(StorageMslConstructs.MappingFragmentElement, _xmlNamespace);
481 writer.WriteAttributeString(StorageMslConstructs.EntityTypeMappingStoreEntitySetAttribute, store.Name);
482 foreach (EdmProperty storeProperty in store.ElementType.Properties)
484 // we don't add the fk properties to c-space, so some are missing,
485 // check to see if we have a map for this one
486 if (_lookups.StoreEdmPropertyToModelEdmProperty.ContainsKey(storeProperty))
488 EdmProperty modelProperty = _lookups.StoreEdmPropertyToModelEdmProperty[storeProperty];
489 WriteScalarPropertyElement(writer, storeProperty, modelProperty);
492 writer.WriteEndElement();
495 private void WriteScalarPropertyElement(XmlWriter writer, EdmProperty store, EdmProperty model)
497 Debug.Assert(store.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType, "only expect scalar type properties");
498 Debug.Assert(model.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType, "only expect scalar type properties");
500 writer.WriteStartElement(StorageMslConstructs.ScalarPropertyElement, _xmlNamespace);
501 writer.WriteAttributeString(StorageMslConstructs.ScalarPropertyNameAttribute, model.Name);
502 writer.WriteAttributeString(StorageMslConstructs.ScalarPropertyColumnNameAttribute, store.Name);
503 writer.WriteEndElement();
506 private void WriteMappingStartElement(XmlWriter writer)
508 writer.WriteStartElement(StorageMslConstructs.MappingElement, _xmlNamespace);
509 writer.WriteAttributeString(StorageMslConstructs.MappingSpaceAttribute, "C-S");