/// <summary> /// Creates a CodeDOM namespace Teraque.DataModelGenerator contains the strongly typed DataSet. /// </summary> /// <param name="schema">The schema description of the strongly typed DataSet.</param> public Namespace(DataModelSchema dataModelSchema) { //namespace Teraque.DataModelGenerator //{ this.Name = dataModelSchema.TargetNamespace; // The interface provides the contracts for this service. this.Types.Add(new TargetInterface.TargetInterface(dataModelSchema)); // Types List <CodeTypeDeclaration> typeList = new List <CodeTypeDeclaration>(); typeList.Add(new TenantTargetClass.TenantTargetClass(dataModelSchema)); typeList.Add(new FieldCollectorClass.FieldCollectorClass(dataModelSchema)); typeList.Add(new TargetClass.TargetClass(dataModelSchema)); typeList.Add(new TransactionClass.TransactionClass(dataModelSchema)); typeList.Add(new TransactionLogItemClass.TransactionLogItemClass(dataModelSchema)); typeList.Sort( delegate(CodeTypeDeclaration firstDeclaration, CodeTypeDeclaration secondDeclaration) { return(firstDeclaration.Name.CompareTo(secondDeclaration.Name)); }); foreach (CodeTypeDeclaration codeTypeDeclaration in typeList) { this.Types.Add(codeTypeDeclaration); } //} }
/// <summary> /// Initializes a new instance of the CodeCreateTransactionStatement class. /// </summary> /// <param name="dataModelSchema">The data model schema.</param> /// <param name="transactionExpression">The name of the local variable to which the transaction is assigned.</param> public CodeCreateTransactionStatement(DataModelSchema dataModelSchema, CodeVariableReferenceExpression transactionExpression) { // DataModelTransaction o1881 = DataModel.CurrentTransaction; this.Type = new CodeTypeReference(String.Format("{0}Transaction", dataModelSchema.Name)); this.Name = transactionExpression.VariableName; this.InitExpression = new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(dataModelSchema.Name), "CurrentTransaction"); }
/// <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> /// Creates a CodeDOM namespace Teraque.ClientGenerator contains the strongly typed DataSet. /// </summary> /// <param name="schema">The schema description of the strongly typed DataSet.</param> public Namespace(DataModelSchema dataModelSchema) { //namespace Teraque.ClientGenerator //{ this.Name = dataModelSchema.TargetNamespace; // The target class. this.Types.Add(new Teraque.DataModelGenerator.TargetClass.TargetClass(dataModelSchema)); //} }
/// <summary> /// Generate the code from the custom tool. /// </summary> /// <param name="wszInputFilePath">The name of the input file.</param> /// <param name="bstrInputFileContents">The contents of the input file.</param> /// <param name="wszDefaultNamespace">The namespace Teraque.ClientGenerator the generated code.</param> /// <param name="pbstrOutputFileContents">The generated code.</param> /// <param name="pbstrOutputFileContentSize">The buffer size of the generated code.</param> /// <param name="pGenerateProgress">An indication of the tools progress.</param> /// <returns>0 indicates the tool handled the command.</returns> public int Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] rgbOutputFileContents, out uint pcbOutput, IVsGeneratorProgress pGenerateProgress) { // Throw an execption if there is nothing to process. if (bstrInputFileContents == null) { throw new ArgumentNullException(bstrInputFileContents); } // This schema describes the data model that is to be generated. DataModelSchema dataModelSchema = new DataModelSchema(bstrInputFileContents); dataModelSchema.GeneratorType = typeof(ClientGenerator); dataModelSchema.TargetNamespace = wszDefaultNamespace; // This is where all the work is done to translate the input schema into the CodeDOM for the data model and the CodeDOM // for the interface to that data model. CodeCompileUnit codeCompileUnit = new CodeCompileUnit(); codeCompileUnit.Namespaces.Add(new Namespace(dataModelSchema)); CodeNamespace emptyNamespace = new CodeNamespace(); emptyNamespace.Types.Add(new TargetInterface.TargetInterface(dataModelSchema)); emptyNamespace.Types.Add(new TargetClientClass.TargetClientClass(dataModelSchema)); codeCompileUnit.Namespaces.Add(emptyNamespace); // If a handler was provided for the generation of the code, then call it with an update. if (pGenerateProgress != null) { pGenerateProgress.Progress(50, 100); } // This will generate the target source code in the language described by the CodeDOM provider. StringWriter stringWriter = new StringWriter(); this.codeProvider.GenerateCodeFromCompileUnit(codeCompileUnit, stringWriter, this.codeGeneratorOptions); // If a handler was provided for the progress, then let it know that the task is complete. if (pGenerateProgress != null) { pGenerateProgress.Progress(100, 100); } // This will pack the generated buffer into an unmanaged block of memory that can be passed back to Visual Studio. byte[] generatedBuffer = System.Text.Encoding.UTF8.GetBytes(stringWriter.ToString()); rgbOutputFileContents[0] = Marshal.AllocCoTaskMem(generatedBuffer.Length); Marshal.Copy(generatedBuffer, 0, rgbOutputFileContents[0], generatedBuffer.Length); pcbOutput = (uint)generatedBuffer.Length; // At this point the code generation was a success. return(VSConstants.S_OK); }
/// <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); }
public ObjectSchema(DataModelSchema dataModelSchema, XmlSchemaObject xmlSchemaObject) { // Initialize the object. this.DataModelSchema = dataModelSchema; this.xmlSchemaObject = xmlSchemaObject; }
/// <summary> /// Creates a statement method invocation that adds a record to an ADO transaction. /// </summary> /// <param name="transactionExpression">The MiddleTierContext used for the transaction.</param> /// <param name="columnSchema">The record that is held for the duration of the transaction.</param> public CodeAcquireRecordReaderLockExpression(CodeExpression transactionExpression, CodeExpression rowExpression, DataModelSchema dataModelSchema) { // departmentRow.ObjectRow.AcquireReaderLock(middleTierTransaction.AdoResourceManager.Guid, Teraque.UnitTest.Server.DataModel.lockTimeout); this.Method = new CodeMethodReferenceExpression(rowExpression, "AcquireReaderLock"); this.Parameters.Add(new CodePropertyReferenceExpression(transactionExpression, "TransactionId")); this.Parameters.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(String.Format("Tenant{0}", dataModelSchema.Name)), "lockTimeout")); }
/// <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; }
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: inputFilePath = 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 (inputFilePath == null) { throw new Exception("Usage: SchemaScrubber -i <InputFileName>"); } inputFilePath = Environment.ExpandEnvironmentVariables(inputFilePath); // Expand the environment variables for the outpt file path. if (outputFileName == null) { outputFileName = String.Format("{0}.cs", Path.GetFileNameWithoutExtension(inputFilePath)); } outputFileName = Environment.ExpandEnvironmentVariables(outputFileName); // 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. StreamReader streamReader = new StreamReader(inputFilePath); DataModelSchema 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("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); }
/// <summary> /// Generate the code from the inputs. /// </summary> /// <param name=\"inputFileName\">The name of the input file.</param> /// <param name=\"inputFileContent\">The contents of the input file.</param> /// <returns>A buffer containing the generated code.</returns> private static byte[] GenerateDdl(DataModelSchema dataModelSchema) { // The generated data is written to a memeory stream using a writer. MemoryStream memoryStream = new MemoryStream(); StreamWriter streamWriter = new StreamWriter(memoryStream); // Generate the file header. streamWriter.WriteLine("/*******************************************************************************"); streamWriter.WriteLine("* <auto-generated>"); streamWriter.WriteLine("* This code was generated by a tool."); streamWriter.WriteLine("* Runtime Version:1.0.0.0"); streamWriter.WriteLine("*"); streamWriter.WriteLine("* Changes to this file may cause incorrect behavior and will be lost if"); streamWriter.WriteLine("* the code is regenerated."); streamWriter.WriteLine("* </auto-generated>"); streamWriter.WriteLine("*******************************************************************************/"); streamWriter.WriteLine(); streamWriter.WriteLine("/* Set the environment */"); streamWriter.WriteLine("set nocount on"); streamWriter.WriteLine("set quoted_identifier on"); streamWriter.WriteLine(); // If this is the first version, then generate the version control schema. if (dataModelSchema.Version == 0.0m) { GenerateVersionControl(streamWriter); } // The tables must be written out to the DDL file so that parent tables are written out before child tables. As tables // are emitted into the DDL file, they are removed from the list. This continues until the list of tables is empty. SortedList <string, TableSchema> tables = new SortedList <string, TableSchema>(); foreach (KeyValuePair <string, TableSchema> keyValuePair in dataModelSchema.Tables) { if (keyValuePair.Value.IsPersistent) { tables.Add(keyValuePair.Key, keyValuePair.Value); } } // Write the tables out such that the parent tables are written before the children to prevent forward reference errors // when creating the foreign indices. As tables are emitted to the DDL, they are removed from the list until the list // is empty. while (tables.Count > 0) { foreach (KeyValuePair <string, TableSchema> tablePair in tables) { // This table will be examined to see if it qualified to be emitted to the DDL. All the parents must be // emitted before the children to avoid having the generation of the foreign keys fail. TableSchema tableSchema = tablePair.Value; // This will search the tables to see if the current table has any parent dependancies that haven't been // written yet. bool isParentDefined = true; foreach (KeyValuePair <string, RelationSchema> parentRelationPair in tableSchema.ParentRelations) { if (tables.ContainsKey(parentRelationPair.Value.ParentTable.Name)) { isParentDefined = false; break; } } // If there are parent dependancies that have not yet been generated, then skip this table for now. if (!isParentDefined) { continue; } // The table schema is removed from the list after it is written to the stream. if (tableSchema.IsPersistent) { GenerateTable(streamWriter, tableSchema); } tables.Remove(tablePair.Key); break; } } // This will flush all the generated code into an array of bytes that can be written back to a file. The choice of a // byte array as a means of returning the generated data is intended to emulate the interface of the Visual Studio // tools. streamWriter.Flush(); return(memoryStream.ToArray()); }
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). switch (argument) { case "-i": argumentState = ArgumentState.InputFileName; continue; case "-out": argumentState = ArgumentState.OutputFileName; continue; } // The parsing state will determine which variable is read next. switch (argumentState) { case ArgumentState.InputFileName: DatabaseGenerator.inputFileName = argument; break; case ArgumentState.OutputFileName: DatabaseGenerator.outputFileName = argument; break; } // The default state is to look for the input file name on the command line. argumentState = ArgumentState.InputFileName; } // Throw a usage message back at the user if no file name was given. if (inputFileName == null) { throw new Exception("Usage: \"Database Generator\" -i <input file name> -out <DDL file>"); } inputFileName = Environment.ExpandEnvironmentVariables(inputFileName); // If no output file name was specified, create one from the input file specification. if (outputFileName == null) { outputFileName = String.Format("{0}.sql", Path.GetFileNameWithoutExtension(inputFileName)); } outputFileName = Environment.ExpandEnvironmentVariables(outputFileName); // Read the schema into a string. This emulates the way that the IDE would normally call a code generator. Create // the MiddleTierSchema (like a Schema, but with extra helping functions and relations for this type of code generation). StreamReader streamReader = new StreamReader(inputFileName); // This will generate a buffer of source code from the input schema. DataModelSchema dataModelSchema = new DataModelSchema(streamReader.ReadToEnd()); byte[] buffer = GenerateDdl(dataModelSchema); // Write the buffer to the specified output file. FileStream outputStream = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.Write); outputStream.Write(buffer, 0, buffer.Length); outputStream.SetLength(buffer.Length); outputStream.Close(); } catch (Exception exception) { // Write the error to the console. Console.WriteLine(exception.Message); } return(0); }