这里我们来一步步分析这些部分的含义,这个里面DataMapper主要是用来定义一些C#基础数据类型和数据库生成脚本之间的映射关系。
1 GetTableName接下来我们看看GetTableName这个函数,这里首先来当前Model是否定义了TempTableAttribute,这个看名字就清楚了就是用来定义当前Model是否是用来生成一张临时表的。
/// <summary> /// 是否临时表, 仅限 Dapper 生成 数据库表结构时使用 /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class TempTableAttribute : Attribute { }
具体我们来看看怎样在实体Model中定义TempTableAttribute这个自定义属性。
[TempTable] class StringTable { public string DefaultString { get; set; } [MaxLength(30)] public string LengthString { get; set; } [Required] public string NotNullString { get; set; } }
就像这样定义的话,我们就知道当前Model会生成一张SQL Server的临时表。
当然如果是生成临时表,则会在生成的表名称前面加一个‘#'标志,在这段代码中我们还会去判断当前实体是否定义了TableAttribute,如果定义过就去取这个TableAttribute的名称,否则就去当前Model的名称,这里也举一个实例。
[Table("Test")] class IntTable { public int IntProperty { get; set; } public int? NullableIntProperty { get; set; } }
这样我们通过代码创建的数据库名称就是Test啦。
2 GenerateFields这个主要是用来一个个读取Model中的属性,并将每一个实体属性整理成一个KeyValuePair<string, PropertyInfo>的对象从而方便最后一步来生成整个表完整的脚本,这里也有些内容需要注意,如果当前属性定义了NotMappedAttribute标签,那么我们可以直接跳过当前属性,另外还需要注意的地方就是当前属性的名称首先看当前属性是否定义了ColumnAttribute的如果定义了,那么数据库中字段名称就取自ColumnAttribute定义的名称,否则才是取自当前属性的名称,通过这样一步操作我们就能够将所有的属性读取到一个自定义的数据结构List<KeyValuePair<string, PropertyInfo>>里面去了。
3 GenerateTableScript有了前面的两步准备工作,后面就是进入到生成整个创建表脚本的部分了,其实这里也比较简单,就是通过循环来一个个生成每一个属性对应的脚本,然后通过StringBuilder来拼接到一起形成一个完整的整体。这里面有一点需要我们注意的地方就是当前字段是否可为空还取决于当前属性是否定义过RequiredAttribute标签,如果定义过那么就需要在创建的脚本后面添加Not Null,最后一个重点就是对于string类型的属性我们需要读取其定义的MaxLength属性从而确定数据库中的字段长度,如果没有定义则取默认长度500。
当然一个完整的代码怎么能少得了单元测试呢?下面我们来看看单元测试。
二 单元测试public class SqlServerTableGenerator_Tests { [Table("Test")] class IntTable { public int IntProperty { get; set; } public int? NullableIntProperty { get; set; } } [Fact] public void GenerateTableScript_Int_Number10() { // Act var sql = new TableGenerator().GenerateTableScript(typeof(IntTable)); // Assert sql.ShouldContain("IntProperty NUMBER(10) NOT NULL"); sql.ShouldContain("NullableIntProperty NUMBER(10)"); } [Fact] public void GenerateTableScript_TestTableName_Test() { // Act var sql = new TableGenerator().GenerateTableScript(typeof(IntTable)); // Assert sql.ShouldContain("CREATE TABLE Test"); } [TempTable] class StringTable { public string DefaultString { get; set; } [MaxLength(30)] public string LengthString { get; set; } [Required] public string NotNullString { get; set; } } [Fact] public void GenerateTableScript_TempTable_TableNameWithSharp() { // Act var sql = new TableGenerator().GenerateTableScript(typeof(StringTable)); // Assert sql.ShouldContain("Create Table #StringTable"); } [Fact] public void GenerateTableScript_String_Varchar() { // Act var sql = new TableGenerator().GenerateTableScript(typeof(StringTable)); // Assert sql.ShouldContain("DefaultString VARCHAR2(500 CHAR)"); sql.ShouldContain("LengthString VARCHAR2(30 CHAR)"); sql.ShouldContain("NotNullString VARCHAR2(500 CHAR) NOT NULL"); } class ColumnTable { [Column("Test")] public int IntProperty { get; set; } [NotMapped] public int Ingored {get; set; } } [Fact] public void GenerateTableScript_ColumnName_NewName() { // Act var sql = new TableGenerator().GenerateTableScript(typeof(ColumnTable)); // Assert sql.ShouldContain("Test NUMBER(10) NOT NULL"); } [Fact] public void GenerateTableScript_NotMapped_Ignore() { // Act var sql = new TableGenerator().GenerateTableScript(typeof(ColumnTable)); // Assert sql.ShouldNotContain("Ingored NUMBER(10) NOT NULL"); } class NotSupportedTable { public dynamic Ingored {get; set; } } [Fact] public void GenerateTableScript_NotSupported_ThrowException() { // Act Assert.Throws<NotSupportedException>(() => { new TableGenerator().GenerateTableScript(typeof(NotSupportedTable)); }); } }
最后我们来看看最终生成的创建表的脚本。
1 定义过TableAttribute的脚本。