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); }
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> ConstructEntityTypes( EdmItemCollection edmItemCollection, DbProviderManifest providerManifest, Version targetVersion) { var entityTypes = new List <XElement>(); // Translate the CSDL EntityTypes into SSDL EntityTypes foreach (var csdlEntityType in edmItemCollection.GetAllEntityTypes()) { var entityTypeElement = new XElement( _ssdl + "EntityType", new XAttribute("Name", OutputGeneratorHelpers.GetStorageEntityTypeName(csdlEntityType, edmItemCollection))); // Add the keys if (csdlEntityType.GetRootOrSelf().GetKeyProperties().Any()) { var keyElement = new XElement(_ssdl + "Key"); foreach (EdmMember key in csdlEntityType.GetRootOrSelf().GetKeyProperties()) { keyElement.Add(new XElement(_ssdl + "PropertyRef", new XAttribute("Name", key.Name))); } entityTypeElement.Add(keyElement); } // Add only the properties on the declared type but also add the keys that might be on the root type foreach ( var property in csdlEntityType.Properties.Where(p => (p.DeclaringType == csdlEntityType)) .Union(csdlEntityType.GetRootOrSelf().GetKeyProperties())) { // If we encounter a ComplexType, we need to recursively flatten it out // into a list of all contained properties if (property.IsComplexProperty()) { property.VisitComplexProperty( (namePrefix, nestedProperty) => { var propertyElement = new XElement( _ssdl + "Property", new XAttribute("Name", namePrefix), new XAttribute("Type", nestedProperty.GetStoreType(providerManifest))); // Add StoreGeneratedPattern if it exists, but only add this to the table created from // the root type if (property.DeclaringType == csdlEntityType) { propertyElement.Add(ConstructStoreGeneratedPatternAttribute(nestedProperty, targetVersion)); } // Add the facets foreach (var facet in nestedProperty.InferSsdlFacetsForCsdlProperty(providerManifest)) { if (facet.Value != null) { propertyElement.Add(new XAttribute(facet.Name, facet.Value)); } } // We'll identify extended properties on all nested properties and migrate them. OutputGeneratorHelpers.CopyExtendedPropertiesToSsdlElement(nestedProperty, propertyElement); entityTypeElement.Add(propertyElement); }, "_", true); } else { var propertyElement = new XElement( _ssdl + "Property", new XAttribute("Name", property.Name), new XAttribute("Type", property.GetStoreType(providerManifest))); // Add StoreGeneratedPattern if it exists, but only add this to the table created from // the root type if (property.DeclaringType == csdlEntityType) { propertyElement.Add(ConstructStoreGeneratedPatternAttribute(property, targetVersion)); } // Add the facets foreach (var facet in property.InferSsdlFacetsForCsdlProperty(providerManifest)) { if (facet.Value != null) { var facetValue = facet.Value; // for DateTime attributes, if allow XAttribute to use its default formatting we end up // with attribute such as: // Facet="yyyy-MM-ddTHH:mm:ssZ", but we need Facet="yyyy-MM-dd HH:mm:ss.fffZ" (note the // space instead of 'T' and the fractions of seconds) to be valid SSDL if (typeof(DateTime).Equals(facetValue.GetType())) { facetValue = string.Format( CultureInfo.InvariantCulture, "{0:yyyy'-'MM'-'dd HH':'mm':'ss'.'fff'Z'}", facet.Value); } propertyElement.Add(new XAttribute(facet.Name, facetValue)); } } OutputGeneratorHelpers.CopyExtendedPropertiesToSsdlElement(property, propertyElement); entityTypeElement.Add(propertyElement); } } // 1. If there is a Referential Constraint specified on the C-side then there is no need // to create foreign keys since the dependent end's primary keys are the foreign keys. // 2. In other cases, we will have to infer the foreign keys by examining any associations that // the entity type participates in and add the principal keys as the foreign keys on the // dependent end. foreach (var containedAssociation in edmItemCollection.GetAllAssociations() .Where( a => (a.IsManyToMany() == false) && (a.ReferentialConstraints.Count == 0) && (a.GetDependentEnd().GetEntityType() == csdlEntityType))) { foreach (var keyProperty in containedAssociation.GetPrincipalEnd().GetEntityType().GetKeyProperties()) { var propertyElement = new XElement( _ssdl + "Property", new XAttribute( "Name", OutputGeneratorHelpers.GetFkName( containedAssociation, containedAssociation.GetDependentEnd(), keyProperty.Name)), new XAttribute("Type", keyProperty.GetStoreType(providerManifest))); // Add the facets foreach (var facet in keyProperty.InferSsdlFacetsForCsdlProperty(providerManifest)) { if (facet.Value != null && !facet.Name.Equals("Nullable", StringComparison.OrdinalIgnoreCase)) { propertyElement.Add(new XAttribute(facet.Name, facet.Value)); } } // The Nullability of this property is dependent on the multiplicity of the association. // If the principal end's multiplicity is 0..1, then this property is Nullable. propertyElement.Add( new XAttribute( "Nullable", containedAssociation.GetPrincipalEnd().RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne)); entityTypeElement.Add(propertyElement); } } OutputGeneratorHelpers.CopyExtendedPropertiesToSsdlElement(csdlEntityType, entityTypeElement); entityTypes.Add(entityTypeElement); } // For all *:* Associations, we need a pair table and an associated pair EntityType foreach (var assoc in edmItemCollection.GetItems <AssociationType>().Where(a => a.IsManyToMany())) { var entityTypeElement = new XElement(_ssdl + "EntityType", new XAttribute("Name", assoc.Name)); // We determine the properties as the aggregation of the primary keys from both ends of the association. // These properties are also the keys of this new EntityTyp var keyElement = new XElement(_ssdl + "Key"); foreach (var key in assoc.GetEnd1().GetKeyProperties()) { keyElement.Add( new XElement( _ssdl + "PropertyRef", new XAttribute("Name", OutputGeneratorHelpers.GetFkName(assoc, assoc.GetEnd2(), key.Name)))); } foreach (var key in assoc.GetEnd2().GetKeyProperties()) { keyElement.Add( new XElement( _ssdl + "PropertyRef", new XAttribute("Name", OutputGeneratorHelpers.GetFkName(assoc, assoc.GetEnd1(), key.Name)))); } entityTypeElement.Add(keyElement); // These are foreign keys as well; we create 0..1 associations so these will be nullable keys foreach (var property in assoc.GetEnd1().GetKeyProperties()) { var propertyElement = new XElement( _ssdl + "Property", new XAttribute("Name", OutputGeneratorHelpers.GetFkName(assoc, assoc.GetEnd2(), property.Name)), new XAttribute("Type", property.GetStoreType(providerManifest))); // Add the facets foreach (var facet in property.InferSsdlFacetsForCsdlProperty(providerManifest)) { if (facet.Value != null) { propertyElement.Add(new XAttribute(facet.Name, facet.Value)); } } entityTypeElement.Add(propertyElement); } foreach (var property in assoc.GetEnd2().GetKeyProperties()) { var propertyElement = new XElement( _ssdl + "Property", new XAttribute("Name", OutputGeneratorHelpers.GetFkName(assoc, assoc.GetEnd1(), property.Name)), new XAttribute("Type", property.GetStoreType(providerManifest))); // Add the facets foreach (var facet in property.InferSsdlFacetsForCsdlProperty(providerManifest)) { if (facet.Value != null) { propertyElement.Add(new XAttribute(facet.Name, facet.Value)); } } entityTypeElement.Add(propertyElement); } entityTypes.Add(entityTypeElement); } return(entityTypes); }