internal static XElement ConstructEntityContainer( EdmItemCollection edm, string databaseSchemaName, string csdlNamespace, string ssdlNamespace) { var entityContainerElement = new XElement( _ssdl + "EntityContainer", new XAttribute("Name", OutputGeneratorHelpers.ConstructStorageEntityContainerName(csdlNamespace))); #region Constructing EntitySets // In TPT, we need to create the SSDL EntitySets from the EntityTypes; we create another table for the derived type. foreach (var entityType in edm.GetAllEntityTypes()) { var entitySetElement = ConstructEntitySet( ssdlNamespace, OutputGeneratorHelpers.GetStorageEntityTypeName(entityType, edm), OutputGeneratorHelpers.GetStorageEntityTypeName(entityType, edm), "Tables", databaseSchemaName); // we would also tack on DefiningQueries here if we wanted entityContainerElement.Add(entitySetElement); } // Find all *:* Associations and create EntitySets in the SSDL foreach (var associationSet in edm.GetAllAssociationSets().Where(set => set.GetAssociation().IsManyToMany())) { var entitySetElement = ConstructEntitySet( ssdlNamespace, associationSet.Name, associationSet.ElementType.Name, "Tables", databaseSchemaName); entityContainerElement.Add(entitySetElement); } #endregion #region Constructing AssociationSets foreach (var associationSet in edm.GetAllAssociationSets()) { var assoc = associationSet.GetAssociation(); if (assoc.GetEnd1() != null && assoc.GetEnd2() != null) { // *:* C-Space associations: we will have two S-space associations bound to the pair table corresponding to each end if (assoc.IsManyToMany()) { // create an association from the first end to the pair table var associationSet1Element = ConstructAssociationSet( ssdlNamespace, OutputGeneratorHelpers.GetStorageAssociationSetNameFromManyToMany(associationSet, assoc.GetEnd1()), OutputGeneratorHelpers.GetStorageAssociationNameFromManyToMany(assoc.GetEnd1()), assoc.GetEnd1().Name, OutputGeneratorHelpers.GetStorageEntityTypeName(assoc.GetEnd1().GetEntityType(), edm), assoc.Name, associationSet.Name); // create an association from the second end to the pair table var associationSet2Element = ConstructAssociationSet( ssdlNamespace, OutputGeneratorHelpers.GetStorageAssociationSetNameFromManyToMany(associationSet, assoc.GetEnd2()), OutputGeneratorHelpers.GetStorageAssociationNameFromManyToMany(assoc.GetEnd2()), assoc.GetEnd2().Name, OutputGeneratorHelpers.GetStorageEntityTypeName(assoc.GetEnd2().GetEntityType(), edm), assoc.Name, associationSet.Name); entityContainerElement.Add(associationSet1Element); entityContainerElement.Add(associationSet2Element); } // All other associations: we essentially mirror the C-space associations else { var associationSetElement = ConstructAssociationSet( ssdlNamespace, associationSet.Name, assoc.Name, assoc.GetEnd1().Name, OutputGeneratorHelpers.GetStorageEntityTypeName(assoc.GetEnd1().GetEntityType(), edm), assoc.GetEnd2().Name, OutputGeneratorHelpers.GetStorageEntityTypeName(assoc.GetEnd2().GetEntityType(), edm)); entityContainerElement.Add(associationSetElement); } } } // Now we will construct AssociationSets with PK:PK associations based off of inheritance foreach (var derivedType in edm.GetAllEntityTypes().Where(et => et.BaseType != null)) { entityContainerElement.Add( ConstructAssociationSet( ssdlNamespace, String.Format( CultureInfo.CurrentCulture, Resources.CodeViewFKConstraintDerivedType, derivedType.Name, derivedType.BaseType.Name), String.Format( CultureInfo.CurrentCulture, Resources.CodeViewFKConstraintDerivedType, derivedType.Name, derivedType.BaseType.Name), derivedType.BaseType.Name, OutputGeneratorHelpers.GetStorageEntityTypeName(derivedType.BaseType as EntityType, edm), derivedType.Name, OutputGeneratorHelpers.GetStorageEntityTypeName(derivedType, edm))); } #endregion var csdlEntityContainer = edm.GetItems <EntityContainer>().FirstOrDefault(); Debug.Assert(csdlEntityContainer != null, "Could not find the CSDL EntityContainer to migrate extended properties"); if (csdlEntityContainer != null) { OutputGeneratorHelpers.CopyExtendedPropertiesToSsdlElement(csdlEntityContainer, entityContainerElement); } return(entityContainerElement); }
internal static List <XElement> ConstructAssociations(EdmItemCollection edm, string ssdlNamespace) { var associations = new List <XElement>(); // Ignore *:* associations for now, just translate the CSDL Associations into SSDL Associations foreach (var association in edm.GetItems <AssociationType>().Where(a => !a.IsManyToMany())) { var associationElement = new XElement(_ssdl + "Association", new XAttribute("Name", association.Name)); var principalEnd = association.GetPrincipalEnd(); var dependentEnd = association.GetDependentEnd(); // 1. If we have a PK:PK relationship that has a ref constraint, then the multiplicity of the // dependent end will always be 0..1. // 2. If we have a PK:PK relationship without a ref constraint, the multiplicity will // always be *. // 3. If we have any other relationship, regardless of the ref constraint, we simply // mirror the multiplicity from the C-side. if (principalEnd != null && dependentEnd != null) { foreach (var end in association.AssociationEndMembers) { var entityType = end.GetEntityType(); var multiplicity = TranslateMultiplicity(end.RelationshipMultiplicity); if (end == dependentEnd && association.IsPKToPK()) { multiplicity = (association.ReferentialConstraints.Count > 0) ? TranslateMultiplicity(RelationshipMultiplicity.ZeroOrOne) : TranslateMultiplicity(RelationshipMultiplicity.Many); } var associationEnd = new XElement( _ssdl + "End", new XAttribute("Role", end.Name), new XAttribute("Type", ssdlNamespace + "." + OutputGeneratorHelpers.GetStorageEntityTypeName(entityType, edm)), new XAttribute("Multiplicity", multiplicity)); // Now we will attempt to add an OnDelete="Cascade" rule if (end.GetOnDelete() == OperationAction.Cascade) { associationEnd.Add( new XElement( _ssdl + "OnDelete", new XAttribute("Action", "Cascade"))); } associationElement.Add(associationEnd); } } // 1. If we have an existing ref constraint in the C-side, then we will simply mirror that in the SSDL. // 2. If we have a non *:* association without a ref constraint, then we specify the foreign keys' names of // the dependent entity type (which is the principal entity type's keys' names) if (association.ReferentialConstraints.Count > 0) { var refConstraint = association.ReferentialConstraints.FirstOrDefault(); if (refConstraint != null) { associationElement.Add( ConstructReferentialConstraintInternal( refConstraint.FromRole.Name, refConstraint.FromProperties.Select(fp => fp.Name), refConstraint.ToRole.Name, refConstraint.ToProperties.Select(tp => tp.Name))); } } else { associationElement.Add( ConstructReferentialConstraint( principalEnd.Name, principalEnd, dependentEnd.Name, dependentEnd)); } OutputGeneratorHelpers.CopyExtendedPropertiesToSsdlElement(association, associationElement); associations.Add(associationElement); } // Now let's tackle the *:* Associations. A *:* conceptual Association means that there is actually a pair table // in the database, and two relationships -- one from the first end to the pair table and another from the pair table // to the second end. // For *:* associations, we'll also identify any extended properties and migrate them to _both_ associations on the S-side. foreach (var m2mAssoc in edm.GetItems <AssociationType>().Where(a => a.IsManyToMany())) { if (m2mAssoc.GetEnd1() != null && m2mAssoc.GetEnd2() != null) { var entityType1 = m2mAssoc.GetEnd1().GetEntityType(); var entityType2 = m2mAssoc.GetEnd2().GetEntityType(); if (entityType1 != null && entityType2 != null) { var associationElement1 = new XElement( _ssdl + "Association", new XAttribute("Name", OutputGeneratorHelpers.GetStorageAssociationNameFromManyToMany(m2mAssoc.GetEnd1()))); associationElement1.Add( new XElement( _ssdl + "End", new XAttribute("Role", m2mAssoc.GetEnd1().Name), new XAttribute( "Type", ssdlNamespace + "." + OutputGeneratorHelpers.GetStorageEntityTypeName(entityType1, edm)), new XAttribute("Multiplicity", "1"))); associationElement1.Add( new XElement( _ssdl + "End", new XAttribute("Role", m2mAssoc.Name), new XAttribute("Type", ssdlNamespace + "." + m2mAssoc.Name), new XAttribute("Multiplicity", "*"))); associationElement1.Add( ConstructReferentialConstraint(m2mAssoc.GetEnd1().Name, m2mAssoc.GetEnd1(), m2mAssoc.Name, m2mAssoc.GetEnd2())); OutputGeneratorHelpers.CopyExtendedPropertiesToSsdlElement(m2mAssoc, associationElement1); associations.Add(associationElement1); var associationElement2 = new XElement( _ssdl + "Association", new XAttribute("Name", OutputGeneratorHelpers.GetStorageAssociationNameFromManyToMany(m2mAssoc.GetEnd2()))); associationElement2.Add( new XElement( _ssdl + "End", new XAttribute("Role", m2mAssoc.Name), new XAttribute("Type", ssdlNamespace + "." + m2mAssoc.Name), new XAttribute("Multiplicity", "*"))); associationElement2.Add( new XElement( _ssdl + "End", new XAttribute("Role", m2mAssoc.GetEnd2().Name), new XAttribute( "Type", ssdlNamespace + "." + OutputGeneratorHelpers.GetStorageEntityTypeName(entityType2, edm)), new XAttribute("Multiplicity", "1"))); associationElement2.Add( ConstructReferentialConstraint(m2mAssoc.GetEnd2().Name, m2mAssoc.GetEnd2(), m2mAssoc.Name, m2mAssoc.GetEnd1())); OutputGeneratorHelpers.CopyExtendedPropertiesToSsdlElement(m2mAssoc, associationElement2); associations.Add(associationElement2); } } } // Finally, we will add PK:PK Associations for any inheritance found in the conceptual. These will translate into PK constraints // in the DDL which will round-trip back in an identifiable way. Base Type's role in this association will have OnDelete action // set to Cascade so that if you delete a row from the base table, any corresponding rows in the child table will also be deleted. foreach (var derivedType in edm.GetAllEntityTypes().Where(et => et.BaseType != null)) { var pkAssociation = new XElement( _ssdl + "Association", new XAttribute( "Name", String.Format( CultureInfo.CurrentCulture, Resources.CodeViewFKConstraintDerivedType, derivedType.Name, derivedType.BaseType.Name))); var baseTypeRole = new XElement( _ssdl + "End", new XAttribute("Role", derivedType.BaseType.Name), new XAttribute( "Type", ssdlNamespace + "." + OutputGeneratorHelpers.GetStorageEntityTypeName(derivedType.BaseType as EntityType, edm)), new XAttribute("Multiplicity", "1")); pkAssociation.Add(baseTypeRole); baseTypeRole.Add(new XElement(_ssdl + "OnDelete", new XAttribute("Action", "Cascade"))); pkAssociation.Add( new XElement( _ssdl + "End", new XAttribute("Role", derivedType.Name), new XAttribute("Type", ssdlNamespace + "." + OutputGeneratorHelpers.GetStorageEntityTypeName(derivedType, edm)), new XAttribute("Multiplicity", "0..1"))); pkAssociation.Add( ConstructReferentialConstraintInternal( derivedType.BaseType.Name, derivedType.GetRootOrSelf().GetKeyProperties().Select(k => k.Name), derivedType.Name, derivedType.GetRootOrSelf().GetKeyProperties().Select(k => k.Name))); associations.Add(pkAssociation); } return(associations); }