/// <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> /// 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 ForeignKeyConstraintSchema(DataModelSchema dataModelSchema, XmlSchemaKeyref xmlSchemaKeyref) : base(dataModelSchema, xmlSchemaKeyref) { // This will search through each of the tables looking for a key that matches the name of the reference. Note that // there is no checking to make sure the 'Refer' key exists. If it didn't exist, the XmlSchema would have caught it // and never have validated the schema. foreach (KeyValuePair <string, TableSchema> keyValuePair in dataModelSchema.Tables) { ConstraintSchema constraintSchema; if (keyValuePair.Value.Constraints.TryGetValue(xmlSchemaKeyref.Refer.Name, out constraintSchema)) { this.relatedTable = constraintSchema.Table; this.relatedColumns = constraintSchema.Columns; } } // Parse the cascading update rule out of the keyref specification. XmlAttribute updateRuleAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaKeyref, QualifiedName.UpdateRule); this.updateRule = updateRuleAttribute == null ? CascadeRules.Cascade : (CascadeRules)Enum.Parse(typeof(CascadeRules), updateRuleAttribute.Value); // Parse the cascading delete rule out of the keyref specification. XmlAttribute deleteRuleAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaKeyref, QualifiedName.DeleteRule); this.deleteRule = deleteRuleAttribute == null ? CascadeRules.Cascade : (CascadeRules)Enum.Parse(typeof(CascadeRules), deleteRuleAttribute.Value); }
/// <summary> /// Creates a constraint that forces a set of columns to be unique. /// </summary> /// <param name="dataModelSchema">The parent data model schema.</param> /// <param name="xmlSchemaUnique">The XmlSchema description of the constraint.</param> public UniqueConstraintSchema(DataModelSchema dataModelSchema, XmlSchemaUnique xmlSchemaUnique) : base(dataModelSchema, xmlSchemaUnique) { // This determines whether the constraint should be used as the primary key for a table. XmlAttribute xmlAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaUnique, QualifiedName.PrimaryKey); this.isPrimaryKey = xmlAttribute == null ? false : Convert.ToBoolean(xmlAttribute.Value); }
/// <summary> /// Constructs a schema from the contents of an XML specification. /// </summary> /// <param name="fileContents">The contents of a file that specifies the schema in XML.</param> public AnnotationSchema(DataModelSchema dataModelSchema, XmlSchemaAnnotation xmlSchemaAnnotation) : base(dataModelSchema, xmlSchemaAnnotation) { this.itemList = new List <Object>(); foreach (XmlSchemaObject xmlSchemaObject in xmlSchemaAnnotation.Items) { if (xmlSchemaObject is XmlSchemaAppInfo) { this.itemList.Add(new AppInfoSchema(dataModelSchema, xmlSchemaObject as XmlSchemaAppInfo)); } } }
/// <summary> /// Create a description of a constraint on a table. /// </summary> /// <param name="schema">The Schema of the entire data model.</param> /// <param name="xmlSchemaIdentityConstraint">The schema of a constraint.</param> public ConstraintSchema(DataModelSchema dataModelSchema, XmlSchemaIdentityConstraint xmlSchemaIdentityConstraint) { // Initialize the object. this.name = xmlSchemaIdentityConstraint.Name; // Pull apart the selector and construct a table name from the XPath specification. Since we flatten out the // hierarchy of the XmlSchema, there is no need to navigate a tree of nodes. Only the table name is important for the // selector and there are no namespaces to be sorted out. string[] selectorXPath = xmlSchemaIdentityConstraint.Selector.XPath.Split(':'); string tableName = selectorXPath.Length == 1 ? selectorXPath[0] : selectorXPath[1]; tableName = tableName.Replace(".", string.Empty); tableName = tableName.Replace("/", string.Empty); this.table = dataModelSchema.Tables[tableName]; // The fields of the constraint are collected in this object. List <ColumnSchema> columnList = new List <ColumnSchema>(); // The raw schema contains the qualified names of all the columns in this constraint. The tables are defined before // the constraints are evaluated, so it's safe to reference the table and its columns while collecting the fields in // the constraint. foreach (XmlSchemaXPath xmlSchemaXPath in xmlSchemaIdentityConstraint.Fields) { // Pull apart the field and construct a qualified name from the XPath specification. The qualified name can be used // to find the equivalent column in the selector's table. string[] fieldXPath = xmlSchemaXPath.XPath.Split(':'); string columnName = fieldXPath.Length == 1 ? fieldXPath[0] : fieldXPath[1]; if (!this.table.Columns.ContainsKey(columnName)) { throw new Exception(string.Format("The column {0} doesn't belong to the table {1} in constraint {2}", columnName, this.table.Name, xmlSchemaIdentityConstraint.Name)); } columnList.Add(this.table.Columns[columnName]); } // The list of fields is converted to an array of columns for the lifetime of this object. this.columns = columnList.ToArray(); // This indicates that all of the elements of the constraint can be nulled. this.isNullable = true; foreach (ColumnSchema columnSchema in this.columns) { if (!columnSchema.IsNullable) { this.isNullable = false; } } }
/// <summary> /// Create a table schema from the XML Schema specification. /// </summary> /// <param name="dataModelSchema">The data model to which this table belongs.</param> /// <param name="xmlSchemaElement">The root of the XmlSchema element that describes the table.</param> public TableSchema(DataModelSchema dataModelSchema, XmlSchemaElement xmlSchemaElement) : base(dataModelSchema, xmlSchemaElement) { // Initialize the object. this.dataModelSchema = dataModelSchema; this.isPersistent = GetPersistentIndicator(xmlSchemaElement); this.name = xmlSchemaElement.Name; this.columnList = new SortedList <string, ColumnSchema>(); this.constraintList = new SortedList <string, ConstraintSchema>(); this.childRelationList = new SortedList <string, RelationSchema>(); this.parentRelationList = new SortedList <string, RelationSchema>(); // Every table has a row version column which tracks the history of changes to the row. ColumnSchema rowVersionSchema = new ColumnSchema(this, "RowVersion", typeof(long), false, true, DBNull.Value, int.MaxValue); this.columnList.Add(rowVersionSchema.Name, rowVersionSchema); // Initialize the columns of the table. Initialize(xmlSchemaElement); }
/// <summary> /// Initializes the column from an XmlSchemaElement or XmlSchemaAttribute. /// </summary> /// <param name="xmlSchemaAnnotated">An XmlSchema object containing the attributes of the column.</param> private void Initialize(XmlSchemaAnnotated xmlSchemaAnnotated) { // Extract the column properties from an XmlSchemaElement. if (xmlSchemaAnnotated is XmlSchemaElement) { // This element is used to describe the column. XmlSchemaElement xmlSchemaElement = xmlSchemaAnnotated as XmlSchemaElement; // This information is taken directly from the known XmlSchema attributes. The rest of the information about the // column needs to be extracted using unhandled attributes and element facets. this.name = xmlSchemaElement.Name; this.isNullable = xmlSchemaElement.MinOccurs == 0.0m; // Extract the string that contains the type information from the 'DataType' custom attribute. This is a // Microsoft extension to the XML Schema definition. XmlAttribute dataTypeAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaElement, QualifiedName.DataType); if (dataTypeAttribute != null) { // The type information is stored as a string. This will rip apart the string to get the type, // assembly, version, etc. information that can be used to access the type through reflection. string typeName = dataTypeAttribute.Value; string[] parts = typeName.Split(','); // There's no a lot of fancy parsing going on here. If it doesn't match the expected format, then // reject the type. if (parts.Length != 5) { throw new Exception(string.Format("Can't analyze the data type found on line {0}, {1}", xmlSchemaElement.LineNumber, typeName)); } // This will load the assembly into memory and use reflection to get the data type that was specified // in the XML file as a string. string assemblyFullName = string.Format("{0},{1},{2},{3}", parts[1], parts[2], parts[3], parts[4]); Assembly assembly = Assembly.Load(assemblyFullName); if (assembly == null) { throw new Exception(string.Format("Unable to load the type {0} from assembly {1}", parts[0], assemblyFullName)); } this.dataType = assembly.GetType(parts[0]); if (this.dataType == null) { throw new Exception(string.Format("Unable to load the type {0} from assembly {1}", parts[0], assemblyFullName)); } } else { // This will extract the simple data type and the maximum field length from the facets associated with the element. if (xmlSchemaElement.ElementSchemaType is XmlSchemaSimpleType) { XmlSchemaSimpleType xmlSchemaSimpleType = xmlSchemaElement.ElementSchemaType as XmlSchemaSimpleType; this.dataType = xmlSchemaSimpleType.Datatype.ValueType; if (xmlSchemaSimpleType.Content is XmlSchemaSimpleTypeRestriction) { XmlSchemaSimpleTypeRestriction xmlSchemaSimpleTypeRestriction = xmlSchemaSimpleType.Content as XmlSchemaSimpleTypeRestriction; foreach (XmlSchemaFacet xmlSchemaFacet in xmlSchemaSimpleTypeRestriction.Facets) { if (xmlSchemaFacet is XmlSchemaMaxLengthFacet) { XmlSchemaMaxLengthFacet xmlSchemaMaxLengthFacet = xmlSchemaFacet as XmlSchemaMaxLengthFacet; this.maximumLength = Convert.ToInt32(xmlSchemaMaxLengthFacet.Value); } } } } } // The defalt value can only be evaluated after the data type has been determined. Otherwise, there's no way to // know to which data type the default value is converted. this.defaultValue = DataModelSchema.ConvertValue(this.dataType, xmlSchemaElement.DefaultValue); } // Extract the column properties from an Attribute. if (xmlSchemaAnnotated is XmlSchemaAttribute) { // This attribute describes the column. XmlSchemaAttribute xmlSchemaAttribute = xmlSchemaAnnotated as XmlSchemaAttribute; // This information is taken directly from the known XmlSchema attributes. The rest of the information about the // column needs to be extracted using unhandled attributes and element facets. this.name = xmlSchemaAttribute.Name; this.dataType = xmlSchemaAttribute.AttributeSchemaType.Datatype.ValueType; this.defaultValue = DataModelSchema.ConvertValue(this.dataType, xmlSchemaAttribute.DefaultValue); } // Determine the IsEncryptedColumn property. XmlAttribute isColumnEncrytedAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaAnnotated, QualifiedName.IsEncrypted); this.isEncrypted = isColumnEncrytedAttribute == null ? false : Convert.ToBoolean(isColumnEncrytedAttribute.Value); // Determine the IsIdentityColumn property. XmlAttribute autoIncrementAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaAnnotated, QualifiedName.AutoIncrement); this.isAutoIncrement = autoIncrementAttribute == null ? false : Convert.ToBoolean(autoIncrementAttribute.Value); // Determine the IsPersistent property. XmlAttribute isColumnPersistentAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaAnnotated, QualifiedName.IsPersistent); this.isPersistent = isColumnPersistentAttribute == null ? true : Convert.ToBoolean(isColumnPersistentAttribute.Value); // Determine the AutoIncrementSeed property. XmlAttribute autoIncrementSeedAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaAnnotated, QualifiedName.AutoIncrementSeed); this.autoIncrementSeed = autoIncrementSeedAttribute == null ? 0 : Convert.ToInt32(autoIncrementSeedAttribute.Value); // Determine the AutoIncrementStop property XmlAttribute autoIncrementStepAttribute = ObjectSchema.GetUnhandledAttribute(xmlSchemaAnnotated, QualifiedName.AutoIncrementStep); this.autoIncrementStep = autoIncrementStepAttribute == null ? 0 : Convert.ToInt32(autoIncrementStepAttribute.Value); }
/// <summary> /// Constructs a schema from the contents of an XML specification. /// </summary> /// <param name="fileContents">The contents of a file that specifies the schema in XML.</param> public AppInfoSchema(DataModelSchema dataModelSchema, XmlSchemaAppInfo xmlSchemaAppInfo) : base(dataModelSchema, xmlSchemaAppInfo) { this.source = xmlSchemaAppInfo.Source; this.markup = xmlSchemaAppInfo.Markup; }
public ObjectSchema(DataModelSchema dataModelSchema, XmlSchemaObject xmlSchemaObject) { // Initialize the object. this.DataModelSchema = dataModelSchema; this.xmlSchemaObject = xmlSchemaObject; }
static int Main(string[] args) { try { // The command line parser is driven by different states that are triggered by the flags read. Unless a flag has // been read, the command line parser assumes that it's reading the file name from the command line. argumentState = ArgumentState.InputFileName; // Parse the command line for arguments. foreach (string argument in args) { // Decode the current argument into a state change (or some other action). if (argument == "-i") { argumentState = ArgumentState.InputFileName; continue; } if (argument == "-out") { argumentState = ArgumentState.OutputFileName; continue; } // The parsing state will determine which variable is read next. switch (argumentState) { case ArgumentState.InputFileName: inputFileName = argument; break; case ArgumentState.OutputFileName: outputFileName = argument; break; } // The default state is to look for the input file name on the command line. argumentState = ArgumentState.InputFileName; } // Expand the environment variables for the input file path. if (inputFileName == null) { throw new Exception("Usage: SchemaScrubber -i <InputFileName> -out <OutputFileName>"); } inputFileName = Environment.ExpandEnvironmentVariables(inputFileName); // Expand the environment variables for the outpt file path. if (outputFileName == null) { outputFileName = inputFileName; } // The main idea here is to emulate the interface that Visual Studio uses to generate a file. The first step is to // read the schema from the input file into a string. This emulates the way that the IDE would normally call a // code generator. Then create a 'MiddleTierGenerator' to compile the schema. DataModelSchema dataModelSchema; using (StreamReader streamReader = new StreamReader(inputFileName)) dataModelSchema = new DataModelSchema(streamReader.ReadToEnd()); // Regurgitate the schema as an XDocument XDocument xDocument = new XDocument(); XElement schemaElement = new XElement( SchemaScrubber.xs + "schema", new XAttribute("id", dataModelSchema.Name), new XAttribute("targetNamespace", dataModelSchema.TargetNamespace), new XAttribute(XNamespace.Xmlns + "mstns", "http://tempuri.org/DataModel.xsd"), new XAttribute("xmlns", "http://tempuri.org/DataModel.xsd"), new XAttribute(XNamespace.Xmlns + "xs", "http://www.w3.org/2001/XMLSchema"), new XAttribute(XNamespace.Xmlns + "msdata", "urn:schemas-microsoft-com:xml-msdata"), new XAttribute(XNamespace.Xmlns + "msprop", "urn:schemas-microsoft-com:xml-msprop"), new XAttribute(XNamespace.Xmlns + "tdata", "urn:schemas-teraque-com:xml-tdata"), new XAttribute("attributeFormDefault", "qualified"), new XAttribute("elementFormDefault", "qualified") ); // Emit Annotations foreach (ObjectSchema itemSchema0 in dataModelSchema.Items) { if (itemSchema0 is AnnotationSchema) { AnnotationSchema annotationSchema = itemSchema0 as AnnotationSchema; XElement annotationElement = new XElement(SchemaScrubber.xs + "annotation"); foreach (ObjectSchema itemSchema1 in annotationSchema.Items) { if (itemSchema1 is AppInfoSchema) { AppInfoSchema appInfoSchema = itemSchema1 as AppInfoSchema; XElement appInfoElement = new XElement(SchemaScrubber.xs + "appinfo"); if (appInfoSchema.Source != String.Empty) { appInfoElement.Add(new XAttribute("source", appInfoSchema.Source)); } foreach (XmlNode xmlNode in appInfoSchema.Markup) { appInfoElement.Add(XElement.Parse(xmlNode.OuterXml)); } annotationElement.Add(appInfoElement); } } schemaElement.Add(annotationElement); } } // Data Model // <xs:element name="DataModel" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" msprop:Generator_UserDSName="DataModel" // msprop:Generator_DataSetName="DataModel" msprop:EnableTableAdapterManager="true"> XElement dataModelElement = new XElement( SchemaScrubber.xs + "element", new XAttribute("name", dataModelSchema.Name), new XAttribute(msdata + "IsDataSet", true), new XAttribute(msdata + "UseCurrentLocale", true), new XAttribute(SchemaScrubber.msprop + "Generator_UserDSName", dataModelSchema.Name), new XAttribute(SchemaScrubber.msprop + "Generator_DataSetName", dataModelSchema.Name), new XAttribute(SchemaScrubber.msprop + "EnableTableAdapterManager", true)); // <xs:complexType> XElement dataModelComlexTypeElement = new XElement(SchemaScrubber.xs + "complexType"); // <xs:choice minOccurs="0" maxOccurs="unbounded"> XElement dataModelChoices = new XElement( SchemaScrubber.xs + "choice", new XAttribute("minOccurs", 0), new XAttribute("maxOccurs", "unbounded")); // This will scrub and add each of the tables to the schema in alphabetical order. foreach (KeyValuePair <String, TableSchema> keyValuePair in dataModelSchema.Tables) { dataModelChoices.Add(CreateTable(keyValuePair.Value)); } // The complex types that define the tables. dataModelComlexTypeElement.Add(dataModelChoices); dataModelElement.Add(dataModelComlexTypeElement); // This will order the primary keys. List <UniqueConstraintSchema> primaryKeyList = new List <UniqueConstraintSchema>(); foreach (KeyValuePair <String, TableSchema> keyValuePair in dataModelSchema.Tables) { primaryKeyList.AddRange(keyValuePair.Value.UniqueConstraintSchemas); } primaryKeyList.Sort((first, second) => { return(first.Name.CompareTo(second.Name)); }); foreach (UniqueConstraintSchema uniqueConstraintSchema in primaryKeyList) { dataModelElement.Add(CreateUniqueKey(uniqueConstraintSchema)); } // This will order the foreign primary keys. List <ForeignKeyConstraintSchema> foreignKeyList = new List <ForeignKeyConstraintSchema>(); foreach (KeyValuePair <String, TableSchema> keyValuePair in dataModelSchema.Tables) { foreignKeyList.AddRange(keyValuePair.Value.ForeignKeyConstraintSchemas); } foreignKeyList.Sort((first, second) => { return(first.Name.CompareTo(second.Name)); }); foreach (ForeignKeyConstraintSchema foreignConstraintSchema in foreignKeyList) { dataModelElement.Add(CreateForeignKey(foreignConstraintSchema)); } // Add the data model element to the document. schemaElement.Add(dataModelElement); // Create the document from the root. xDocument.Add(schemaElement); // Save the regurgitated output. xDocument.Save(outputFileName); } catch (Exception exception) { // Dump any exceptions to the console. Console.WriteLine(exception.Message); } // At this point the code generated created the code-behind for the source schema successfully. return(0); }