Code First 代码优先,数据迁移

注意点

1.CodeFirst可以快速开发,使用起来也要分阶段使用,比如早期随便搞,中后期需要禁用一些功能保证数据安全(标题6和7 )

2.数据库账号需要有比较高的权限,

3.Sqlite不支持删除列和修改列只能添加列


1、入门示例

会根据连接字符串中的数据库进行创建

//建库:如果不存在创建数据库存在不会重复创建 
db.DbMaintenance.CreateDatabase(); // 注意 :Oracle和个别国产库需不支持该方法,需要手动建库 


//创建表:根据实体类CodeFirstTable1  (所有数据库都支持)    
db.CodeFirst.InitTables(typeof(CodeFirstTable1));//这样一个表就能成功创建了


//保护机质说明:如果不是sqlsugar建的表执行CodeFirst会报错看标题8

//实体定义看2.1


2、创建表&修改表 

2.1 纯特性建表(建表、批量建表)

注意:默认类型是支持多种数据库的,能不设置尽量用自带的,ColumnDataType(想要多库兼容看4.2和9)一般用于特殊情况

public class CodeFirstTable1
{
        [SugarColumn(IsIdentity = true, IsPrimaryKey = true)]
        public int Id { get; set; } 
        public string Name { get; set; }
        //ColumnDataType 自定格式的情况 length不要设置 (想要多库兼容看4.2和9)
        [SugarColumn(ColumnDataType = "Nvarchar(255)")]
        public string Text { get; set; }
        [SugarColumn(IsNullable = true)]//可以为NULL
        public DateTime CreateTime { get; set; }
}

/***创建单个表***/
db.CodeFirst.SetStringDefaultLength(200).InitTables(typeof(CodeFirstTable1));//这样一个表就能成功创建了
/***手动建多个表***/
db.CodeFirst.SetStringDefaultLength(200)
.InitTables(typeof(CodeFirstTable1),typeof(CodeFirstTable2)); 



/***批量创建表***/
//语法1:
Type[] types= Assembly
        .LoadFrom("XXX.dll")//如果 .dll报错,可以换成 xxx.exe 有些生成的是exe 
        .GetTypes().Where(it=>it.FullName.Contains("OrmTest."))//命名空间过滤,当然你也可以写其他条件过滤
        .ToArray();//断点调试一下是不是需要的Type,不是需要的在进行过滤

db.CodeFirst.SetStringDefaultLength(200).InitTables(types);//根据types创建表
         
         
//语法2:                
Type[] types= typeof(任意实体类中的类).Assembly.GetTypes()
.Where(it=>it.FullName.Contains("OrmTest."))//命名空间过滤,当然你也可以写其他条件过滤
.ToArray();
db.CodeFirst.SetStringDefaultLength(200).InitTables(types);//根据types创建表

2.2 建表技巧:自动Nullable 

上面的例子需要加IsNullable=true才能实现创建可空类型,否则是必填类型

我们通过实体AOP配置 进行 自动 int? decimal?处理

    var db = new SqlSugarClient(new ConnectionConfig()
    {
        DbType = SqlSugar.DbType.SqlServer,
        ConnectionString = Config.ConnectionString,
        IsAutoCloseConnection = true,
        ConfigureExternalServices = new ConfigureExternalServices
        {
             //注意:  这儿AOP设置不能少
            EntityService = (c, p) =>
            {
              /***低版本C#写法***/
              // int?  decimal?这种 isnullable=true 不支持string(下面.NET 7支持)
              if (p.IsPrimaryKey==false&&c.PropertyType.IsGenericType &&
              c.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
              {
                p.IsNullable = true;
              }
               
              /***高版C#写法***/
              //支持string?和string  
              if(p.IsPrimaryKey==false&&new NullabilityInfoContext()
               .Create(c).WriteState is NullabilityState.Nullable)
               {
                 //p.IsNullable = true;
               } 
            }
        }
    });
   //定义CodeFirst实体
   public class xxxxx1
   {
           
       //没有?是必填  
       [SugarColumn(IsIdentity = true, IsPrimaryKey = true)]
       public int id { get; set; }
            
       //字符串没办法加? 那就加特性[SugarColumn(IsNullable = true)]
       //如果不想用SugarColumn, AOP里面打个断点能进P.IsNullable=true说明就成功了,用法很灵活逻辑自已控制
       public string RequiredName { get; set; }
                  
       //带?可空 
       public int? id1 { get; set; }
             
  }

2.3 建表技巧:启用下划线 

通过实体AOP实现

 SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
  {
       DbType = DbType.SqlServer,
       ConnectionString = Config.ConnectionString3,
       IsAutoCloseConnection = true,
       ConfigureExternalServices=new ConfigureExternalServices() 
       {
           EntityService = (x,p) => //处理列名
           {
             p.DbColumnName = UtilMethods.ToUnderLine(p.DbColumnName);//ToUnderLine驼峰转下划线方法
           },
           EntityNameService = (x, p) => //处理表名
           {
             p.DbTableName=UtilMethods.ToUnderLine(p.DbTableName);//ToUnderLine驼峰转下划线方法
           }
        }
   });
  
  //表名mysql: test_name , oracle就是 TEST_NAME
  //public class TestName

2.4 无特性建表

想实体类干净可以用无特性写法,从用户使用情况来讲使用特性会更方便

SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
{
    DbType = DbType.SqlServer,
    ConnectionString = Config.ConnectionString3,
    InitKeyType = InitKeyType.Attribute,
    IsAutoCloseConnection = true,
    ConfigureExternalServices = new ConfigureExternalServices()
    {
        EntityService = (s, p) =>
        {
            //如果是Order实体进行相关配置
            p.IfTable<Order>()
            .UpdateProperty(it => it.id, it =>
            {
                it.IsIdentity = true;
                it.IsPrimarykey = true;
            })
            .UpdateProperty(it => it.Name, it => {
                it.Length = 100;
                it.IsNullable = true;
 
            })
            .OneToOne(it => it.Item, nameof(Order.ItemId));
             
            //如果Custom实体进行相关配置
             p.IfTable<Custom>()
             .UpdateProperty(it => it.id, it =>
             {
                it.IsIdentity = true;
                it.IsPrimarykey = true;
             })
              .UpdateProperty(it => it.Text, it => {
                it.DataType= StaticConfig.CodeFirst_BigString;//支持多库的MaxString用法
              })
             
                         
            //可以结合全局逻辑一起使用,下面的和上面的有冲突的话,下面会覆盖上面的
              
             
            //统一设置 nullable等于isnullable=true
            //低版本C#看标题2.2 
             if(p.IsPrimaryKey==false&&new NullabilityInfoContext()
                         .Create(c).WriteState is NullabilityState.Nullable)
             {
                           p.IsNullable = true;
             }
             
              
              
        }
    }
});
//性能说明:
//EntityService 相同实体只会执行一次性不需太操作

2.5 修改表

简单的说就是通过修改实体后,在重新执行CodeFirst进行进行数据库表的修改

注意:到项目数据库有用后一定要禁删除列操作,如果A用户实体因为没有步少几个字段那么在CodeFirst就有删列风险

标题6或者7可以禁止删除列

功能说明
添加列在实体加添加属性
修改列


3种方式:


1、删除列执行CodeFirst后在添加列(兼容性好,缺点数据会消失)


2、更改类型,非空、长度 等执行CodeFirst【标题4有详细说明】

(缺点:特殊情况修改不了比如约束等,需要用方案1 或者手动改库)


3、修改列名保留数据【5.1有详细说明】


Sqlite不支持 修改 (官方没提供修改表的SQL语法)


删除列

在实体删除属性 (标题6,7可以禁用)

Sqlite不支持 (官方没提供修改表的SQL语法)

索引特性设置索引就会建索引,只能手动删除索引【4.3有详细说明】
描述

表名和列都支持看文档 5.2

支持实体生成的XML备注,需要生成XML

高安全级别:禁用更新禁用后表结构不会发生变化  看档 6和7
高安全级别:手动对比迁移 纯手动去处理数据为变更 看文档10

3、强制设置表名

3.1 固定设置表名

[SugarTable("TableName")]//设置表名为TableName
public class CodeFirstTable
{
   [SugarColumn(IsPrimaryKey = true)] 
   public Guid Id { get; set; }
   public string name{get;set;}
}

3.2 动态设置表名

可一个实体建多个表

//新功能  5.0.2.3
db.CodeFirst.As<UnituLong>("UnituLong0011").InitTables<UnituLong>();
//该功能索引名要加占位符
//[SugarIndex("{table}index_codetable1_name",nameof(CodeFirstTable1.Name),OrderByType.Asc)]
 
 
 
//老功能
var newTableName="Order_"+DateTime.Now.ToString("yyyyMMdd");
db.MappingTables.Add("Order", newTableName); // typeof(类).Name 可以拿到类名
db.CodeFirst.InitTables(typeof(Order)); //生成的表名是 newTableName

自动分表:  http://www.donet5.com/Home/Doc?typeId=1201


4、特性功能

4.1 特性列表

名称描述
IsIdentity是否创建自增标识
IsPrimaryKey是否创建主键标识
ColumnName创建数据库字段的名称(默认取实体类属性名称)
ColumnDataType

创建数据库字段的类型

用法1: “varchar(20)” 不需要设置长度

用法2:      不设置该参数   系统会根据C#类型自动生成相应的数据库类型 

用法3:       多库兼容可以用 :看标题9

IsIgnoreORM不处理该列
ColumnDescription备注 表注释 (新版本支持XML文件
Length长度 设成10会生成   xxx类型(10), 没括号的不设置
IsNullable是否可以为null默为false
DecimalDigits精度 如 decimal(18,2) length=18,DecimalDigits=2
OracleSequenceName设置Oracle序列,设置后该列等同于自增列
OldColumnName修改列名用,这样不会新增或者删除列
IndexGroupNameList已弃用 ,新用法看文档4.3
UniqueGroupNameList已弃用, 新用法看文档4.3

注意:有2个属性用处不同

DefaultValue 

IsOnlyIgnoreInsert 

DefaultValue=默认值 用来建表设置字段默认值

IsOnlyIgnoreInsert=true  插入数据时取默认值

很多情况需要2个一起使用

如果只建表不插入数据用1个 

如果建表并且插入数据用2个

4.2  多库支持

如果大量使用 ColumnDataType="nvarchar(50)" 自定义类型将无法很好的支持多种数据库,比如很多库不支持nvarchar

当然你也可以使用AOP替换 ColumnDataType 去实现多库,下面介绍自带的方案



string  大文本

5.1.3.44-preview06  推荐

[SugarColumn(ColumnDataType = StaticConfig.CodeFirst_BigString)]



string 设置长度的字符串


[SugarColumn(Length=10)]

 public string FieldName{ get; set; } 


int 整数


public int FieldName{ get; set; }

 

short 整数小


public short FieldName{ get; set; }

 

long 大数字


public long FieldName{ get; set; }

 

bool  真假


public bool FieldName{ get; set; }

 

decimal  默认


public decimal  FieldName{ get; set; }

 

decimal  自定义


//18,2  18,4 18,6 这几种兼容性好

[SugarColumn(Length=18,DecimalDigits=2)]

public decimal  FieldName{ get; set; } 


DateTime 时间


public DateTime FieldName{ get; set; }


枚举 (数据库存int)


public 枚举 FieldName{ get; set; }


byte[] 二进制


public byte[]  FileInfo{get;set;}

建议:升级到 SqlSugarCore 5.1.3.46-preview09 及以上

对多库支持了比较好


SqlServer特殊配置:和他库不同一般选用Nvarchar,可以使用这个配置让他和其他数据库区分(其他库是varchar)

     DbType = SqlSugar.DbType.SqlServer,
    ConnectionString ="字符串",
    IsAutoCloseConnection = true,

     MoreSettings=new ConnMoreSettings() {

           SqlServerCodeFirstNvarchar= true, 

     }

注意:该功能满足不了你可以看标题9

4.3 创建索引

分表的用户注意了:请升级到5.0.8.6-preview03修复了分表问题

    //普通索引
    [SugarIndex("index_codetable1_name",nameof(CodeFirstTable1.Name),OrderByType.Asc)]
     
    //唯一索引 (true表示唯一索引)
    [SugarIndex("unique_codetable1_CreateTime", nameof(CodeFirstTable1.CreateTime), OrderByType.Desc,true)]
     
    //复合普通索引
    [SugarIndex("index_codetable1_nameid", nameof(CodeFirstTable1.Name), OrderByType.Asc,
                        nameof(CodeFirstTable1.Id),    OrderByType.Desc)]
   
    public class CodeFirstTable1
    {
        [SugarColumn(IsIdentity = true, IsPrimaryKey = true)]
        public int Id { get; set; }
        public string Name { get; set; }
        [SugarColumn(ColumnDataType = "Nvarchar(255)")]//custom
        public string Text { get; set; }
        [SugarColumn(IsNullable = true)]
        public DateTime CreateTime { get; set; }
    }
    //分表的用户注意了:请升级到5.0.8.6-preview03修复了分表问题

给索引名添加占位符

//占位符 {table} {db}

//请升级到 5.0.2.3preivew04
//使用 {db}  进行占位符替换,小写不要有空格
[SugarIndex("{db}index_codetable1_name",nameof(CodeFirstTable1.Name),OrderByType.Asc)]

//表名占位符(自动分表不需要加这个自动的)
[SugarIndex("index_{table}_name",nameof(CodeFirstTable1.Name),OrderByType.Asc)]

索引include 5.1.3.31-preview11

    //不要有空格并且小写等于 include(name,id)
    [SugarIndex("IndexUnituadfasf1_longx{include:name,id}", nameof(longx), OrderByType.Asc)]
    public class Unituadfasf1
    {

        public ulong longx { get; set; }
        public int id { get; set; }
        public string name { get; set; }
    }

5、更多用例

 5.1 修改现有列名

注意1:属性随便写个名字不按规范写就会删掉在创建

注意2:Sqlite不支持修改列操作

注意3:   表特性如果有禁止修改 要先去掉

[SugarColumn( OldColumnName ="Name")]
public string  NewName { get; set; }

 5.2 添加表描述

如果不写特性也可以用XML自动读取备注(生成XML)

db.CodeFirst.InitTables(typeof(CodeFirstTable));
[SugarTable("CodeFirstTable2",TableDescription = "表备注")]//表添加备注
public class CodeFirstTable
{
   [SugarColumn(IsPrimaryKey = true, ColumnDescription="主键")]//列添加备注
   public Guid Id { get; set; }
}

//技巧:表名可以为null,这样就会取实体名
[SugarTable(null,TableDescription = "表备注")]//表添加备注



6、禁止删除列

注意:Sqlite本身就不支持删除列 可以不设置

如果实体类中属性给删掉在执行InitTables那么数据库中的列也会删除,为了避免误操作把列删掉。下面支持了禁止删除列

禁止删列:

设置成下面的写法 就不会自动删除列了

[SugarTable("Custom","客户",IsDisabledDelete =true)]
 public class Custom
 {
     public int Id { get; set; }
     public string Name { get; set; }
 }  

全局AOP全部禁止

    DbType = SqlSugar.DbType.PostgreSQL,
    ConnectionString = Config.ConnectionString,
    ConfigureExternalServices=new ConfigureExternalServices() {
        EntityNameService = (type, entity) => 
        {
          entity.IsDisabledDelete = true;
         }
    }


7、禁止更新+删除

这样设置后会大大提高CodeFirst的执行性能,因为不用去检查实体数据库变化,也不会更新数据库

IsDisabledUpdateAll=true 那么表存在就不会在执行任何更新操作

[SugarTable("Custom","客户",IsDisabledUpdateAll=true)]//安全级别比 IsDisabledDelete 更高,只创建不修改和删除
public class Custom
{
      public int Id { get; set; }
      public string Name { get; set; }
}
db.CodeFirst.SetStringDefaultLength(200).InitTables(typeof(Custom)); //注意这行代码加的

//上面配置等同于
//if(表不存在)
//{
//  db.CodeFirst.SetStringDefaultLength(200).InitTables(typeof(Custom));  
///}


8、更新其他来源的表

默认会报错的也算是一个保护机质,让你无法更新不是SQLSUGAR建的表,你可以试着把非SQLSUGAR建的表主键约束删掉在试试,如果还不行就说明不能更新,如果没有重要数据就把表删了在建吧


9、自定义类型多库兼容

注意:先看标题4.2如果不能满足我们可以使用AOP动态处理

注意:先看标题4.2如果不能满足我们可以使用AOP动态处理

注意:先看标题4.2如果不能满足我们可以使用AOP动态处理

如果使用自定义类型我们就需要向下面这么写

[SugarColumn(ColumnDataType = "varchar(max)")]

上面的只支持sqlserver(max mysql不支持)下面我们来兼容mysql

   var db = new SqlSugarClient(new ConnectionConfig()
       {
                DbType = SqlSugar.DbType.SqlServer,
                ConnectionString = Config.ConnectionString,
                IsAutoCloseConnection = true,
                ConfigureExternalServices = new ConfigureExternalServices
                {
                    EntityService = (c, p) =>
                    {
                        if (type==SqlSugar.DbType.MySql&&p.DataType == "varchar(max)") 
                        {
                            p.DataType = "longtext";
                        }
                    }
                }
            });


10、结构对比手动迁移

该功能适合成熟,线上稳定的项目,这样不会去自动通过实体修改表结构,而是手工处理

新功能:5.0.5.8 preview02

Type [] types= xxxx;
var diffString= db.CodeFirst.GetDifferenceTables(types).ToDiffString();

我们可以通过结构对比拿到差异信息

image.png

根据这些信息我们可以手动去库里面更改


11、设置创建表排序

在不设置排序是按实体类顺序进行创建表,特殊情况下手动进行排序

规则说明

1、主键反无论设置不设置都是第1位

2、如果不设默认为0

3、如果想只设置一个字段的情况想排前面 可以设置-1 这种负数,默认为0

5.0.9.6版本

[SqlSugar.SugarTable("Custom", IsCreateTableFiledSort =true)]//开启排序不能少
public class Custom
{
    [SqlSugar.SugarColumn(CreateTableFieldSort = 1)]
    public int Id { get; set; }
    [SqlSugar.SugarColumn( CreateTableFieldSort =2)]
    public string Name { get; set; }
}


文档:SqlSugar5.0