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;
17 
18 namespace System.Data.Entity.Design.Common
19 {
20     internal class OneToOneMappingSerializer
21     {
22         internal class MappingLookups
23         {
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>();
27 
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>();
32 
33             internal List<CollapsedEntityAssociationSet> CollapsedEntityAssociationSets = new List<CollapsedEntityAssociationSet>();
34 
35             internal List<Tuple<EdmFunction, EdmFunction>> StoreFunctionToFunctionImport = new List<Tuple<EdmFunction, EdmFunction>>();
36         }
37 
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
44         {
45             private EntitySet _storeEntitySet;
46             private List<AssociationSet> _storeAssociationSets = new List<AssociationSet>(2);
47             private AssociationSet _modelAssociationSet;
48 
49             public AssociationSet ModelAssociationSet
50             {
51                 get { return _modelAssociationSet; }
52                 set
53                 {
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;
56                 }
57             }
58 
CollapsedEntityAssociationSet(EntitySet entitySet)59             public CollapsedEntityAssociationSet(EntitySet entitySet)
60             {
61                 Debug.Assert(entitySet != null, "entitySet parameter is null");
62                 _storeEntitySet = entitySet;
63             }
64 
65             public EntitySet EntitySet
66             {
67                 get { return _storeEntitySet; }
68             }
69 
70             public List<AssociationSet> AssociationSets
71             {
72                 get { return _storeAssociationSets; }
73             }
74 
GetStoreAssociationSetEnd(int index, out AssociationSetEnd storeAssociationSetEnd, out RelationshipMultiplicity multiplicity, out OperationAction deleteBehavior)75             public void GetStoreAssociationSetEnd(int index, out AssociationSetEnd storeAssociationSetEnd, out RelationshipMultiplicity multiplicity, out OperationAction deleteBehavior)
76             {
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);
80             }
81 
GetFromAssociationSetEnd(AssociationSet definingSet, AssociationSet multiplicitySet, out AssociationSetEnd associationSetEnd, out RelationshipMultiplicity multiplicity, out OperationAction deleteBehavior)82             private void GetFromAssociationSetEnd(AssociationSet definingSet, AssociationSet multiplicitySet, out AssociationSetEnd associationSetEnd, out RelationshipMultiplicity multiplicity, out OperationAction deleteBehavior)
83             {
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
101                 //
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
113                 //
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
117                 //
118 
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)
124                 {
125                     OperationAction otherEndBehavior = GetAssociationSetEnd(definingSet, false).CorrespondingAssociationEndMember.DeleteBehavior;
126                     if(otherEndBehavior == OperationAction.None)
127                     {
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;
132                     }
133                 }
134             }
135 
GetAssociationSetEnd(AssociationSet set, bool fromEnd)136             private static AssociationSetEnd GetAssociationSetEnd(AssociationSet set, bool fromEnd)
137             {
138                 Debug.Assert(set.ElementType.ReferentialConstraints.Count == 1, "no referenctial constraint for association[0]");
139                 ReferentialConstraint constraint = set.ElementType.ReferentialConstraints[0];
140 
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)
144                 {
145                     fromEndIndex = 0;
146                     toEndIndex = 1;
147 
148                 }
149                 else
150                 {
151                     fromEndIndex = 1;
152                     toEndIndex = 0;
153                 }
154 
155 
156                 if (fromEnd)
157                 {
158                     return set.AssociationSetEnds[fromEndIndex];
159                 }
160                 else
161                 {
162                     return set.AssociationSetEnds[toEndIndex];
163                 }
164             }
165 
166             public bool MeetsRequirementsForCollapsableAssociation
167             {
168                 get
169                 {
170                     if (_storeAssociationSets.Count != 2)
171                         return false;
172 
173                     ReferentialConstraint constraint0;
174                     ReferentialConstraint constraint1;
175                     GetConstraints(out constraint0, out constraint1);
176                     if (!IsEntityDependentSideOfBothAssociations(constraint0, constraint1))
177                         return false;
178 
179                     if (!IsAtLeastOneColumnOfBothDependentRelationshipColumnSetsNonNullable(constraint0, constraint1))
180                         return false;
181 
182                     if (!AreAllEntityColumnsMappedAsToColumns(constraint0, constraint1))
183                         return false;
184 
185                     if (IsAtLeastOneColumnFKInBothAssociations(constraint0, constraint1))
186                         return false;
187 
188                     return true;
189                 }
190             }
191 
IsAtLeastOneColumnFKInBothAssociations(ReferentialConstraint constraint0, ReferentialConstraint constraint1)192             private bool IsAtLeastOneColumnFKInBothAssociations(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
193             {
194                 return constraint1.ToProperties.Any(c => constraint0.ToProperties.Contains(c));
195             }
196 
IsAtLeastOneColumnOfBothDependentRelationshipColumnSetsNonNullable(ReferentialConstraint constraint0, ReferentialConstraint constraint1)197             private bool IsAtLeastOneColumnOfBothDependentRelationshipColumnSetsNonNullable(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
198             {
199                 return ToPropertyHasNonNullableColumn(constraint0) && ToPropertyHasNonNullableColumn(constraint1);
200             }
201 
ToPropertyHasNonNullableColumn(ReferentialConstraint constraint)202             private static bool ToPropertyHasNonNullableColumn(ReferentialConstraint constraint)
203             {
204                 foreach (EdmProperty property in constraint.ToProperties)
205                 {
206                     if (!property.Nullable)
207                     {
208                         return true;
209                     }
210                 }
211                 return false;
212             }
213 
AreAllEntityColumnsMappedAsToColumns(ReferentialConstraint constraint0, ReferentialConstraint constraint1)214             private bool AreAllEntityColumnsMappedAsToColumns(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
215             {
216                 Set<string> names = new Set<string>();
217                 AddToPropertyNames(constraint0, names);
218                 AddToPropertyNames(constraint1, names);
219                 return names.Count == _storeEntitySet.ElementType.Properties.Count;
220             }
221 
AddToPropertyNames(ReferentialConstraint constraint, Set<string> names)222             private static void AddToPropertyNames(ReferentialConstraint constraint, Set<string> names)
223             {
224                 foreach (EdmProperty property in constraint.ToProperties)
225                 {
226                     names.Add(property.Name);
227                 }
228             }
229 
IsEntityDependentSideOfBothAssociations(ReferentialConstraint constraint0, ReferentialConstraint constraint1)230             private bool IsEntityDependentSideOfBothAssociations(ReferentialConstraint constraint0, ReferentialConstraint constraint1)
231             {
232                 return ((RefType)constraint0.ToRole.TypeUsage.EdmType).ElementType == _storeEntitySet.ElementType && ((RefType)constraint1.ToRole.TypeUsage.EdmType).ElementType == _storeEntitySet.ElementType;
233             }
234 
GetConstraints(out ReferentialConstraint constraint0, out ReferentialConstraint constraint1)235             private void GetConstraints(out ReferentialConstraint constraint0, out ReferentialConstraint constraint1)
236             {
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];
242             }
243         }
244 
245         private MappingLookups _lookups;
246         private EntityContainer _storeContainer;
247         private EntityContainer _modelContainer;
248         private string _xmlNamespace;
249 
OneToOneMappingSerializer(MappingLookups lookups, EntityContainer storeContainer, EntityContainer modelContainer, Version schemaVersion)250         internal OneToOneMappingSerializer(MappingLookups lookups,
251             EntityContainer storeContainer,
252             EntityContainer modelContainer,
253             Version schemaVersion)
254         {
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);
262         }
263 
WriteXml(XmlWriter writer)264         public void WriteXml(XmlWriter writer)
265         {
266             EDesignUtil.CheckArgumentNull(writer, "writer");
267 
268             WriteMappingStartElement(writer);
269             WriteEntityContainerMappingElement(writer);
270             writer.WriteEndElement();
271         }
272 
WriteEntityContainerMappingElement(XmlWriter writer)273         private void WriteEntityContainerMappingElement(XmlWriter writer)
274         {
275             writer.WriteStartElement(StorageMslConstructs.EntityContainerMappingElement, _xmlNamespace);
276             writer.WriteAttributeString(StorageMslConstructs.StorageEntityContainerAttribute, _storeContainer.Name);
277             writer.WriteAttributeString(StorageMslConstructs.CdmEntityContainerAttribute, _modelContainer.Name);
278 
279             foreach (EntitySet set in _lookups.StoreEntitySetToModelEntitySet.Keys)
280             {
281                 EntitySet modelEntitySet = _lookups.StoreEntitySetToModelEntitySet[set];
282                 WriteEntitySetMappingElement(writer, set, modelEntitySet);
283             }
284 
285             foreach(AssociationSet set in _lookups.StoreAssociationSetToModelAssociationSet.Keys)
286             {
287                 AssociationSet modelAssociationSet = _lookups.StoreAssociationSetToModelAssociationSet[set];
288                 WriteAssociationSetMappingElement(writer, set, modelAssociationSet);
289             }
290 
291             foreach (CollapsedEntityAssociationSet set in _lookups.CollapsedEntityAssociationSets)
292             {
293                 WriteAssociationSetMappingElement(writer, set);
294             }
295 
296             foreach (var functionMapping in _lookups.StoreFunctionToFunctionImport)
297             {
298                 var storeFunction = functionMapping.Item1;
299                 var functionImport = functionMapping.Item2;
300                 WriteFunctionImportMappingElement(writer, storeFunction, functionImport);
301             }
302 
303             writer.WriteEndElement();
304         }
305 
WriteFunctionImportMappingElement(XmlWriter writer, EdmFunction storeFunction, EdmFunction functionImport)306         private void WriteFunctionImportMappingElement(XmlWriter writer, EdmFunction storeFunction, EdmFunction functionImport)
307         {
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");
312 
313             writer.WriteStartElement(StorageMslConstructs.FunctionImportMappingElement, _xmlNamespace);
314             writer.WriteAttributeString(StorageMslConstructs.FunctionImportMappingFunctionNameAttribute, storeFunction.FullName);
315             writer.WriteAttributeString(StorageMslConstructs.FunctionImportMappingFunctionImportNameAttribute, functionImport.Name);
316 
317             RowType tvfReturnType = TypeHelpers.GetTvfReturnType(storeFunction);
318             if (tvfReturnType != null)
319             {
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;
325 
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)
331                 {
332                     EdmProperty modelProperty = _lookups.StoreEdmPropertyToModelEdmProperty[storeProperty];
333                     WriteScalarPropertyElement(writer, storeProperty, modelProperty);
334                 }
335                 writer.WriteEndElement();
336                 writer.WriteEndElement();
337             }
338             else
339             {
340                 Debug.Fail("Only TVF store functions are supported.");
341             }
342 
343             writer.WriteEndElement();
344         }
345 
WriteAssociationSetMappingElement(XmlWriter writer, CollapsedEntityAssociationSet collapsedAssociationSet)346         private void WriteAssociationSetMappingElement(XmlWriter writer, CollapsedEntityAssociationSet collapsedAssociationSet)
347         {
348             if (!collapsedAssociationSet.ModelAssociationSet.ElementType.IsForeignKey)
349             {
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);
354 
355 
356                 for (int i = 0; i < collapsedAssociationSet.AssociationSets.Count; i++)
357                 {
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);
364                 }
365 
366                 // don't need condition element
367 
368                 writer.WriteEndElement();
369             }
370         }
371 
WriteAssociationSetMappingElement(XmlWriter writer, AssociationSet store, AssociationSet model)372         private void WriteAssociationSetMappingElement(XmlWriter writer, AssociationSet store, AssociationSet model)
373         {
374             if (!model.ElementType.IsForeignKey)
375             {
376                 writer.WriteStartElement(StorageMslConstructs.AssociationSetMappingElement, _xmlNamespace);
377                 writer.WriteAttributeString(StorageMslConstructs.AssociationSetMappingNameAttribute, model.Name);
378                 writer.WriteAttributeString(StorageMslConstructs.AssociationSetMappingTypeNameAttribute, model.ElementType.FullName);
379 
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);
384 
385                 foreach (AssociationSetEnd storeEnd in store.AssociationSetEnds)
386                 {
387                     AssociationSetEnd modelEnd = _lookups.StoreAssociationSetEndToModelAssociationSetEnd[storeEnd];
388                     WriteEndPropertyElement(writer, storeEnd, modelEnd);
389                 }
390 
391                 ReferentialConstraint constraint = GetReferentialConstraint(store);
392                 foreach (EdmProperty fkColumn in constraint.ToProperties)
393                 {
394                     if (fkColumn.Nullable)
395                     {
396                         WriteConditionElement(writer, fkColumn);
397                     }
398                 }
399 
400                 writer.WriteEndElement();
401             }
402         }
403 
WriteConditionElement(XmlWriter writer, EdmProperty fkColumn)404         private void WriteConditionElement(XmlWriter writer, EdmProperty fkColumn)
405         {
406             writer.WriteStartElement(StorageMslConstructs.ConditionElement, _xmlNamespace);
407             writer.WriteAttributeString(StorageMslConstructs.ConditionColumnNameAttribute, fkColumn.Name);
408             writer.WriteAttributeString(StorageMslConstructs.ConditionIsNullAttribute, "false");
409             writer.WriteEndElement();
410         }
411 
GetAssociationSetEndForForeignKeyTable(AssociationSet store)412         private static AssociationSetEnd GetAssociationSetEndForForeignKeyTable(AssociationSet store)
413         {
414             ReferentialConstraint constraint = GetReferentialConstraint(store);
415             return store.AssociationSetEnds.GetValue(constraint.ToRole.Name, false);
416         }
417 
GetReferentialConstraint(AssociationSet set)418         internal static ReferentialConstraint GetReferentialConstraint(AssociationSet set)
419         {
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)
423             {
424                 Debug.Assert(constraint == null, "we should only get one");
425                 constraint = rc;
426             }
427             Debug.Assert(constraint != null, "we should get at least one constraint");
428             return constraint;
429         }
430 
WriteEndPropertyElement(XmlWriter writer, AssociationSetEnd store, AssociationSetEnd model)431         private void WriteEndPropertyElement(XmlWriter writer, AssociationSetEnd store, AssociationSetEnd model)
432         {
433             writer.WriteStartElement(StorageMslConstructs.EndPropertyMappingElement, _xmlNamespace);
434             writer.WriteAttributeString(StorageMslConstructs.EndPropertyMappingNameAttribute, model.Name);
435             foreach (EdmProperty storeKeyMember in store.EntitySet.ElementType.KeyMembers)
436             {
437                 EdmProperty modelKeyMember = _lookups.StoreEdmPropertyToModelEdmProperty[storeKeyMember];
438                 EdmProperty storeFkTableMember = GetAssociatedFkColumn(store, storeKeyMember);
439                 WriteScalarPropertyElement(writer, storeFkTableMember, modelKeyMember);
440             }
441             writer.WriteEndElement();
442         }
443 
GetAssociatedFkColumn(AssociationSetEnd store, EdmProperty storeKeyProperty)444         private static EdmProperty GetAssociatedFkColumn(AssociationSetEnd store, EdmProperty storeKeyProperty)
445         {
446             ReferentialConstraint constraint = GetReferentialConstraint(store.ParentAssociationSet);
447             if (store.Name == constraint.FromRole.Name)
448             {
449                 for (int i = 0; i < constraint.FromProperties.Count; i++)
450                 {
451                     if (constraint.FromProperties[i] == storeKeyProperty)
452                     {
453                         // return the matching Fk column
454                         return constraint.ToProperties[i];
455                     }
456                 }
457             }
458 
459                 return storeKeyProperty;
460         }
461 
WriteEntitySetMappingElement(XmlWriter writer, EntitySet store, EntitySet model)462         private void WriteEntitySetMappingElement(XmlWriter writer, EntitySet store, EntitySet model)
463         {
464             writer.WriteStartElement(StorageMslConstructs.EntitySetMappingElement, _xmlNamespace);
465             writer.WriteAttributeString(StorageMslConstructs.EntitySetMappingNameAttribute, model.Name);
466             WriteEntityTypeMappingElement(writer, store, model);
467             writer.WriteEndElement();
468         }
469 
WriteEntityTypeMappingElement(XmlWriter writer, EntitySet store, EntitySet model)470         private void WriteEntityTypeMappingElement(XmlWriter writer, EntitySet store, EntitySet model)
471         {
472             writer.WriteStartElement(StorageMslConstructs.EntityTypeMappingElement, _xmlNamespace);
473             writer.WriteAttributeString(StorageMslConstructs.EntityTypeMappingTypeNameAttribute, model.ElementType.FullName);
474             WriteMappingFragmentElement(writer, store, model);
475             writer.WriteEndElement();
476         }
477 
WriteMappingFragmentElement(XmlWriter writer, EntitySet store, EntitySet model)478         private void WriteMappingFragmentElement(XmlWriter writer, EntitySet store, EntitySet model)
479         {
480             writer.WriteStartElement(StorageMslConstructs.MappingFragmentElement, _xmlNamespace);
481             writer.WriteAttributeString(StorageMslConstructs.EntityTypeMappingStoreEntitySetAttribute, store.Name);
482             foreach (EdmProperty storeProperty in store.ElementType.Properties)
483             {
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))
487                 {
488                     EdmProperty modelProperty = _lookups.StoreEdmPropertyToModelEdmProperty[storeProperty];
489                     WriteScalarPropertyElement(writer, storeProperty, modelProperty);
490                 }
491             }
492             writer.WriteEndElement();
493         }
494 
WriteScalarPropertyElement(XmlWriter writer, EdmProperty store, EdmProperty model)495         private void WriteScalarPropertyElement(XmlWriter writer, EdmProperty store, EdmProperty model)
496         {
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");
499 
500             writer.WriteStartElement(StorageMslConstructs.ScalarPropertyElement, _xmlNamespace);
501             writer.WriteAttributeString(StorageMslConstructs.ScalarPropertyNameAttribute, model.Name);
502             writer.WriteAttributeString(StorageMslConstructs.ScalarPropertyColumnNameAttribute, store.Name);
503             writer.WriteEndElement();
504         }
505 
WriteMappingStartElement(XmlWriter writer)506         private void WriteMappingStartElement(XmlWriter writer)
507         {
508             writer.WriteStartElement(StorageMslConstructs.MappingElement, _xmlNamespace);
509             writer.WriteAttributeString(StorageMslConstructs.MappingSpaceAttribute, "C-S");
510         }
511     }
512 }
513