EFCore 通过实体Model生成创建SQL Server数据库表脚本
在我们的项目中经常采用ModelFirst这种方式先来设计数据库Model,然后通过Migration来生成数据库表结构,有些时候我们需要动态通过实体Model来创建数据库的表结构,特别是在创建像临时表这一类型的时候,我们直接通过代码来进行创建就可以了不用通过创建实体然后迁移这种方式来进行,其实原理也很简单就是通过遍历当前Model然后获取每一个属性并以此来生成部分创建脚本,然后将这些创建的脚本拼接成一个完整的脚本到数据库中去执行就可以了,只不过这里有一些需要注意的地方,下面我们来通过代码来一步步分析怎么进行这些代码规范编写以及需要注意些什么问题。
一 代码分析
//////Model生成数据库表脚本 /// publicclassTableGenerator:ITableGenerator{ privatestaticDictionaryDataMapper{ get{ vardataMapper=newDictionary { {typeof(int),"NUMBER(10)NOTNULL"}, {typeof(int?),"NUMBER(10)"}, {typeof(string),"VARCHAR2({0}CHAR)"}, {typeof(bool),"NUMBER(1)"}, {typeof(DateTime),"DATE"}, {typeof(DateTime?),"DATE"}, {typeof(float),"FLOAT"}, {typeof(float?),"FLOAT"}, {typeof(decimal),"DECIMAL(16,4)"}, {typeof(decimal?),"DECIMAL(16,4)"}, {typeof(Guid),"CHAR(36)"}, {typeof(Guid?),"CHAR(36)"} }; returndataMapper; } } privatereadonlyList >_fields=newList >(); /// /// /// privatestring_tableName; ////// /// ///privatestringGetTableName(MemberInfoentityType){ if(_tableName!=null) return_tableName; varprefix=entityType.GetCustomAttribute ()!=null?"#":string.Empty; return_tableName=$"{prefix}{entityType.GetCustomAttribute ()?.Name??entityType.Name}"; } /// ///生成创建表的脚本 /// ///publicstringGenerateTableScript(TypeentityType){ if(entityType==null) thrownewArgumentNullException(nameof(entityType)); GenerateFields(entityType); constintDefaultColumnLength=500; varscript=newStringBuilder(); script.AppendLine($"CREATETABLE{GetTableName(entityType)}("); foreach(var(propName,propertyInfo)in_fields){ if(!DataMapper.ContainsKey(propertyInfo.PropertyType)) thrownewNotSupportedException($"尚不支持{propertyInfo.PropertyType},请联系开发人员."); if(propertyInfo.PropertyType==typeof(string)){ varmaxLengthAttribute=propertyInfo.GetCustomAttribute (); script.Append($"\t{propName}{string.Format(DataMapper[propertyInfo.PropertyType],maxLengthAttribute?.Length??DefaultColumnLength)}"); if(propertyInfo.GetCustomAttribute ()!=null) script.Append("NOTNULL"); script.AppendLine(","); }else{ script.AppendLine($"\t{propName}{DataMapper[propertyInfo.PropertyType]},"); } } script.Remove(script.Length-1,1); script.AppendLine(")"); returnscript.ToString(); } privatevoidGenerateFields(TypeentityType){ foreach(varpinentityType.GetProperties()){ if(p.GetCustomAttribute ()!=null) continue; varcolumnName=p.GetCustomAttribute ()?.Name??p.Name; varfield=newKeyValuePair (columnName,p); _fields.Add(field); } } }
这里的TableGenerator继承自接口ITableGenerator,在这个接口内部只定义了一个 string GenerateTableScript(Type entityType)方法。
//////Model生成数据库表脚本 /// publicinterfaceITableGenerator{ //////生成创建表的脚本 /// ///stringGenerateTableScript(TypeentityType); }
这里我们来一步步分析这些部分的含义,这个里面DataMapper主要是用来定义一些C#基础数据类型和数据库生成脚本之间的映射关系。
1 GetTableName
接下来我们看看GetTableName这个函数,这里首先来当前Model是否定义了TempTableAttribute,这个看名字就清楚了就是用来定义当前Model是否是用来生成一张临时表的。
//////是否临时表,仅限Dapper生成数据库表结构时使用 /// [AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct)] publicclassTempTableAttribute:Attribute{ }
具体我们来看看怎样在实体Model中定义TempTableAttribute这个自定义属性。
[TempTable] classStringTable{ publicstringDefaultString{get;set;} [MaxLength(30)] publicstringLengthString{get;set;} [Required] publicstringNotNullString{get;set;} }
就像这样定义的话,我们就知道当前Model会生成一张SQLServer的临时表。
当然如果是生成临时表,则会在生成的表名称前面加一个‘#'标志,在这段代码中我们还会去判断当前实体是否定义了TableAttribute,如果定义过就去取这个TableAttribute的名称,否则就去当前Model的名称,这里也举一个实例。
[Table("Test")] classIntTable{ publicintIntProperty{get;set;} publicint?NullableIntProperty{get;set;} }
这样我们通过代码创建的数据库名称就是Test啦。
2 GenerateFields
这个主要是用来一个个读取Model中的属性,并将每一个实体属性整理成一个KeyValuePair
3 GenerateTableScript
有了前面的两步准备工作,后面就是进入到生成整个创建表脚本的部分了,其实这里也比较简单,就是通过循环来一个个生成每一个属性对应的脚本,然后通过StringBuilder来拼接到一起形成一个完整的整体。这里面有一点需要我们注意的地方就是当前字段是否可为空还取决于当前属性是否定义过RequiredAttribute标签,如果定义过那么就需要在创建的脚本后面添加NotNull,最后一个重点就是对于string类型的属性我们需要读取其定义的MaxLength属性从而确定数据库中的字段长度,如果没有定义则取默认长度500。
当然一个完整的代码怎么能少得了单元测试呢?下面我们来看看单元测试。
二 单元测试
publicclassSqlServerTableGenerator_Tests{ [Table("Test")] classIntTable{ publicintIntProperty{get;set;} publicint?NullableIntProperty{get;set;} } [Fact] publicvoidGenerateTableScript_Int_Number10(){ //Act varsql=newTableGenerator().GenerateTableScript(typeof(IntTable)); //Assert sql.ShouldContain("IntPropertyNUMBER(10)NOTNULL"); sql.ShouldContain("NullableIntPropertyNUMBER(10)"); } [Fact] publicvoidGenerateTableScript_TestTableName_Test(){ //Act varsql=newTableGenerator().GenerateTableScript(typeof(IntTable)); //Assert sql.ShouldContain("CREATETABLETest"); } [TempTable] classStringTable{ publicstringDefaultString{get;set;} [MaxLength(30)] publicstringLengthString{get;set;} [Required] publicstringNotNullString{get;set;} } [Fact] publicvoidGenerateTableScript_TempTable_TableNameWithSharp(){ //Act varsql=newTableGenerator().GenerateTableScript(typeof(StringTable)); //Assert sql.ShouldContain("CreateTable#StringTable"); } [Fact] publicvoidGenerateTableScript_String_Varchar(){ //Act varsql=newTableGenerator().GenerateTableScript(typeof(StringTable)); //Assert sql.ShouldContain("DefaultStringVARCHAR2(500CHAR)"); sql.ShouldContain("LengthStringVARCHAR2(30CHAR)"); sql.ShouldContain("NotNullStringVARCHAR2(500CHAR)NOTNULL"); } classColumnTable{ [Column("Test")] publicintIntProperty{get;set;} [NotMapped] publicintIngored{get;set;} } [Fact] publicvoidGenerateTableScript_ColumnName_NewName(){ //Act varsql=newTableGenerator().GenerateTableScript(typeof(ColumnTable)); //Assert sql.ShouldContain("TestNUMBER(10)NOTNULL"); } [Fact] publicvoidGenerateTableScript_NotMapped_Ignore(){ //Act varsql=newTableGenerator().GenerateTableScript(typeof(ColumnTable)); //Assert sql.ShouldNotContain("IngoredNUMBER(10)NOTNULL"); } classNotSupportedTable{ publicdynamicIngored{get;set;} } [Fact] publicvoidGenerateTableScript_NotSupported_ThrowException(){ //Act Assert.Throws(()=>{ newTableGenerator().GenerateTableScript(typeof(NotSupportedTable)); }); } }
最后我们来看看最终生成的创建表的脚本。
1 定义过TableAttribute的脚本。
CREATETABLETest( IntPropertyNUMBER(10)NOTNULL, NullableIntPropertyNUMBER(10), )
2 生成的临时表的脚本。
CREATETABLE#StringTable( DefaultStringVARCHAR2(500CHAR), LengthStringVARCHAR2(30CHAR), NotNullStringVARCHAR2(500CHAR)NOTNULL, )
通过这种方式我们就能够在代码中去动态生成数据库表结构了。
以上就是EFCore通过实体Model生成创建SQLServer数据库表脚本的详细内容,更多关于EFCore创建SQLServer数据库表脚本的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。