/// <summary> /// Create a foreign key constraint on two tables. /// </summary> /// <param name="dataModelSchema">The parent data model schema.</param> /// <param name="xmlSchemaKeyref">The XmlSchema object that describes the foreignn key relation.</param> public RelationSchema(DataModelSchema dataModelSchema, XmlSchemaKeyref xmlSchemaKeyref) { // Initialize the object. this.name = xmlSchemaKeyref.Name; // This will search through each of the tables looking for the parent and child components of the relation. foreach (KeyValuePair <string, TableSchema> keyValuePair in dataModelSchema.Tables) { ConstraintSchema constraintSchema; // This is the parent component of the relation. if (keyValuePair.Value.Constraints.TryGetValue(xmlSchemaKeyref.Refer.Name, out constraintSchema)) { UniqueConstraintSchema uniqueConstraintSchema = constraintSchema as UniqueConstraintSchema; this.parentColumns = uniqueConstraintSchema.Columns; this.parentTable = uniqueConstraintSchema.Table; this.parentKeyConstraint = uniqueConstraintSchema; } // This is the child part of the relation. if (keyValuePair.Value.Constraints.TryGetValue(xmlSchemaKeyref.Name, out constraintSchema)) { ForeignKeyConstraintSchema foreignKeyConstraintSchema = constraintSchema as ForeignKeyConstraintSchema; this.childTable = foreignKeyConstraintSchema.Table; this.childColumns = foreignKeyConstraintSchema.Columns; this.childKeyConstraint = foreignKeyConstraintSchema; } } }
/// <summary> /// Creates an array of values from a unique constraint that can be used for finding records. /// </summary> /// <param name="uniqueConstraintSchema">A description of a unique constraint.</param> /// <returns>An array of expressions that can be used as a key for finding records in a table.</returns> public CodeExpression[] CreateKey(UniqueConstraintSchema uniqueConstraintSchema) { // This will cycle through all the foreign and simple parameters looking for any columns that match up to the child // columns of the constraint. When found, they are placed in the array in the proper order to match up against the // given unique constraint. List <CodeExpression> keys = new List <CodeExpression>(); foreach (ColumnSchema uniqueColumn in uniqueConstraintSchema.Columns) { foreach (KeyValuePair <string, ExternalParameterItem> parameterPair in this.ExternalParameterItems) { // This correlates the unique constraint columns with the variables that have been created in the method to // hold the key values from the foreign tables. These variables exist outside of the conditional logic that // finds the parent row, so if the parent key is null, these values will also be null. However, since they're // still declared, they can be used to construct keys, which is useful when they're optional values. if (parameterPair.Value is ForeignKeyConstraintParameterItem) { ForeignKeyConstraintParameterItem foreignKeyConstraintParameterItem = parameterPair.Value as ForeignKeyConstraintParameterItem; ForeignKeyConstraintSchema foreignKeyConstraintSchema = foreignKeyConstraintParameterItem.ForeignKeyConstraintSchema; for (int columnIndex = 0; columnIndex < foreignKeyConstraintSchema.Columns.Length; columnIndex++) { if (uniqueColumn == foreignKeyConstraintSchema.Columns[columnIndex]) { foreach (ForeignKeyVariableItem foreignKeyVariableItem in foreignKeyConstraintParameterItem.ForeignKeyVariables) { if (foreignKeyConstraintSchema.Columns[columnIndex] == foreignKeyVariableItem.ColumnSchema) { keys.Add(foreignKeyVariableItem.Expression); } } } } } // This will match the columns described in the simple parameters to the columns in the unique constraint. if (parameterPair.Value is SimpleParameterItem) { SimpleParameterItem simpleParameterItem = parameterPair.Value as SimpleParameterItem; if (uniqueColumn == simpleParameterItem.ColumnSchema) { keys.Add(new CodeVariableReferenceExpression(CommonConversion.ToCamelCase(simpleParameterItem.ColumnSchema.Name))); } } } } // This array can be used as a key to find the record in a table. return(keys.ToArray()); }
/// <summary> /// The foreign constraints can only be evaluated after all the tables, keys and unique constraints have been evaluated. /// </summary> /// <param name="xmlSchema"></param> private void SecondPass(XmlSchemaSet xmlSchemaSet) { // This is the second pass through the schemas. Once the tables, keys and unique constraints have been evaluated, // then the foreign constraints can be constructed and applied to the parent and child tables. foreach (XmlSchemaElement xmlSchemaElement in xmlSchemaSet.GlobalElements.Values) { // Only the Microsoft DataSet element is evaluated for foreign keys. if (ObjectSchema.IsDataSetElement(xmlSchemaElement)) { // This will examine each of the constraints looking for a foreign key description. foreach (XmlSchemaIdentityConstraint xmlSchemaIdentityConstraint in xmlSchemaElement.Constraints) { // Evaluate the foreign keys in the data model. if (xmlSchemaIdentityConstraint is XmlSchemaKeyref) { // This object can be used as a foreign key constraint and, optionally, can be used to describe a // parent/child relationship. XmlSchemaKeyref xmlSchemaKeyref = xmlSchemaIdentityConstraint as XmlSchemaKeyref; // This creates a foreign key. ForeignKeyConstraintSchema foreignKeyConstraintSchema = new ForeignKeyConstraintSchema(this, xmlSchemaIdentityConstraint as XmlSchemaKeyref); // Foreign constraint schemas are always added to the list of constraints on a table. They can also // conditionally become the source for a relationship between two tables. foreignKeyConstraintSchema.Table.Add(foreignKeyConstraintSchema); // Unless specifically instructed to supress the relation, it will be created add added to both the // parent and child tables as well as the data model. XmlAttribute isConstraintOnlyAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaIdentityConstraint, QualifiedName.ConstraintOnly); if (isConstraintOnlyAttribute == null || !Convert.ToBoolean(isConstraintOnlyAttribute.Value)) { RelationSchema relationSchema = new RelationSchema(this, xmlSchemaKeyref); relationSchema.ParentTable.ChildRelations.Add(relationSchema.Name, relationSchema); relationSchema.ChildTable.ParentRelations.Add(relationSchema.Name, relationSchema); this.Relations.Add(relationSchema.Name, relationSchema); } } } } } }
/// <summary> /// Creates the element that describes a foreign constraint. /// </summary> /// <param name="foreignConstraintSchema">A description of a foreign constraint.</param> /// <returns>An element that can be used in an XML Schema document to describe a foreign constraint.</returns> private static XElement CreateForeignKey(ForeignKeyConstraintSchema foreignKeyConstraintSchema) { // <xs:keyref name="FK_Entity_AccessControl" refer="EntityKey" msprop:rel_Generator_UserRelationName="FK_Entity_AccessControl" // msprop:rel_Generator_RelationVarName="relationFK_Entity_AccessControl" msprop:rel_Generator_UserChildTable="AccessControl" // msprop:rel_Generator_UserParentTable="Entity" msprop:rel_Generator_ParentPropName="EntityRow" // msprop:rel_Generator_ChildPropName="GetAccessControlRows"> // <xs:selector xpath=".//mstns:AccessControl" /> // <xs:field xpath="mstns:EntityId" /> // </xs:keyref> XElement foreignElement = new XElement( SchemaScrubber.xs + "keyref", new XAttribute("name", foreignKeyConstraintSchema.Name), new XAttribute("refer", foreignKeyConstraintSchema.RelatedTable.GetUniqueConstraint(foreignKeyConstraintSchema.RelatedColumns).Name), new XAttribute(SchemaScrubber.msprop + "rel_Generator_UserRelationName", foreignKeyConstraintSchema.Name), new XAttribute(SchemaScrubber.msprop + "rel_Generator_RelationVarName", String.Format("relation{0}", foreignKeyConstraintSchema.Name)), new XAttribute(SchemaScrubber.msprop + "rel_Generator_UserChildTable", foreignKeyConstraintSchema.Table.Name), new XAttribute(SchemaScrubber.msprop + "rel_Generator_UserParentTable", foreignKeyConstraintSchema.RelatedTable.Name), new XAttribute(SchemaScrubber.msprop + "rel_Generator_ParentPropName", String.Format("{0}Row", foreignKeyConstraintSchema.RelatedTable.Name)), new XAttribute(SchemaScrubber.msprop + "rel_Generator_ChildPropName", String.Format("Get{0}Rows", foreignKeyConstraintSchema.Table.Name))); foreignElement.Add( new XElement( SchemaScrubber.xs + "selector", new XAttribute("xpath", String.Format(".//mstns:{0}", foreignKeyConstraintSchema.Table.Name)))); foreach (ColumnSchema columnSchema in foreignKeyConstraintSchema.Columns) { foreignElement.Add( new XElement( SchemaScrubber.xs + "field", new XAttribute("xpath", String.Format("mstns:{0}", columnSchema.Name)))); } // This describes a foreign constraint on a table. return(foreignElement); }