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