MIS中DB是骨干,于是稍有不慎,一个项目的代码就过度依赖于DB的特性,失去了软件的独立性。当有一天打算换个DB类型时,发现不可替换。
DB核心在其建模,但DB不应该成为核心,核心是建模,而不是DB的模。
当然,更换DB类型不可行的原因不是模的问题,而是层的问题,太多与DB有关的代码夹杂到了业务层或者表示层去了。
于是我这样分层:
表示层、业务层、数据层、数据存取层、DB
DB只是一个用于保存数据的地方,MIS是功能驱动,而不是TABLE驱动。
VS也有建模工具,不过是依赖于DB进行的正向工程,把DB的模用DATASET表现出来。
但软件要保持其独立性,应该自己建模。在DATASET中建模,像VISIO那样,然后反向工程,从DATASET生成SQL,随后生成DB,坚决让DB只与存取层打交道,软件可以随时切换为其它DB类型,只不过是修改一下存取层而已。
DATASET建模的意义还在于可以生成任意类型的代码。有一款软件叫动软,作用是根据各类数据库模型生成项目,也支持自定义模板来生成代码。很早就关注,但最近开始使用后觉得不怎么方便,自己此前也用VS代码写过一款很僵化的代码生成工具,思想就是基于表生成代码。不过真的很僵化。
上一次使用词法后,觉得用词法是最终的解决办法。不过这一回还没有用到词法,词法的目的是让人脑做人脑做的事,电脑就让它去做电脑应该做的事吧。
先完成一个简单版本,模板的语法还在优化中。
以下是一个DATASET,两个表
<?xml version="1.0" encoding="utf-16"?> <xs:schema id="DataSetTest" targetNamespace="http://tempuri.org/DataSetTest.xsd" xmlns:mstns="http://tempuri.org/DataSetTest.xsd" xmlns="http://tempuri.org/DataSetTest.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified"> <xs:element name="DataSetTest" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="TestA"> <xs:complexType> <xs:sequence> <xs:element name="A" msdata:Caption="主键列" type="xs:int" /> <xs:element name="B" msdata:Caption="字符列" minOccurs="0"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:maxLength value="30" /> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name="C" msdata:Caption="日期列" type="xs:dateTime" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="TestB"> <xs:complexType> <xs:sequence> <xs:element name="AA" msdata:Caption="主键列" type="xs:int" /> <xs:element name="BB" msdata:Caption="字符列" minOccurs="0"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:maxLength value="30" /> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name="CC" msdata:Caption="日期列" type="xs:dateTime" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> <xs:unique name="Constraint1" msdata:PrimaryKey="true"> <xs:selector xpath=".//mstns:TestA" /> <xs:field xpath="mstns:A" /> </xs:unique> <xs:unique name="TestB_Constraint1" msdata:ConstraintName="Constraint1" msdata:PrimaryKey="true"> <xs:selector xpath=".//mstns:TestB" /> <xs:field xpath="mstns:AA" /> </xs:unique> </xs:element> </xs:schema>
利用SQL生成模板生成SQL,原理是使用解析器对模板进行解析,然后产生C#代码后再动态执行得到最终结果
//qoushui project template code{. IDataSet2Sql DS2Sql =new DataSet2MSSql(); foreach (System.Data.DataTable table in ds.Tables) { .} create table @table.TableName ( code{. for (int colIndex=0;colIndex<table.Columns.Count;colIndex++) { System.Data.DataColumn column = table.Columns[colIndex]; .} @column.ColumnName @DS2Sql.GetDataTypeString(column) @colIndex==table.Columns.Count-1?"":"," code{. } .} ) ; code{. } .}
//qoushui project template create table TestA ( A int , B nvarchar(30) , C datetime ) ; create table TestB ( AA int , BB nvarchar(30) , CC datetime ) ;
利用另一个模板生成实体
//qoushui project template for 实体类 code{. IDataSet2Sql DS2Sql =new DataSet2MSSql(); foreach (System.Data.DataTable table in ds.Tables) { .} /// <summary>@table.TableName /// 自动生成的代码,请勿修改 /// </summary> public partial class @table.TableName :I@table.TableName {{ #region 属性 code{. for (int colIndex=0;colIndex<table.Columns.Count;colIndex++) { System.Data.DataColumn column = table.Columns[colIndex]; .} /// <summary> /// @column.Caption /// </summary> /// <remarks>@column.Caption </remarks> public @column.DataType.ToString() @column.ColumnName {{ get; set; }} code{. } .} #endregion #region 方法 #endregion }} code{. } .}
//qoushui project template for 实体类 /// <summary>TestA /// 自动生成的代码,请勿修改 /// </summary> public partial class TestA :ITestA { #region 属性 /// <summary> /// 主键列 /// </summary> /// <remarks>主键列 </remarks> public System.Int32 A { get; set; } /// <summary> /// 字符列 /// </summary> /// <remarks>字符列 </remarks> public System.String B { get; set; } /// <summary> /// 日期列 /// </summary> /// <remarks>日期列 </remarks> public System.DateTime C { get; set; } #endregion #region 方法 #endregion } /// <summary>TestB /// 自动生成的代码,请勿修改 /// </summary> public partial class TestB :ITestB { #region 属性 /// <summary> /// 主键列 /// </summary> /// <remarks>主键列 </remarks> public System.Int32 AA { get; set; } /// <summary> /// 字符列 /// </summary> /// <remarks>字符列 </remarks> public System.String BB { get; set; } /// <summary> /// 日期列 /// </summary> /// <remarks>日期列 </remarks> public System.DateTime CC { get; set; } #endregion #region 方法 #endregion }