SqlSugar ORM教程 #

建议

我们精心制作本教程,方便用户快速掌握.net orm开发!根据我们的经验,通过此教程,刚入行的1年也可以顺利的进入开发,所以请耐心完成本教程学习(有一定经验的,可以忽略熟悉的章节)。

框架介绍 #

SqlSugar是一款 老牌 .NET 开源ORM框架,由果糖大数据科技团队维护和更新 ,

1. .NET中唯一支持全自动分表组件,SAAS分库,大数据处理的ORM

2. .NET 百万级写入、更新 性能最强框架

3. 使用最简单的ORM 【文档,视频教程 (opens new window)

4.媲美原生的极限的性能

5.Github star数仅次于EF 和 Dapper,每月nuget下载量超过1万

更多优点: 简单易用、功能齐全、高性能、轻量级、服务齐全、官网教程文档、有专业技术支持一天18小时服务

**支持数据库:**MySql、SqlServer、Sqlite、Oracle 、 postgresql、达梦、人大金仓、神通数据库

超前理念 #

SqlSugar是一款来自未来的ORM,拥有超前的理念,需求领跑第一线,可以毫不夸张的说,在设计理念上就算不更新几年都不会过时,我们每天都会跟踪用户需求,将这些用户需求分类和整理,把有共性的功能都整理出来,经历过长达7年的努力,需求成负增长,已经走向了成熟和完善,是一款真正用了功能齐全的ORM框架,如果你用过EF CORE或者DAPPER肯定会为功能缺失而无奈,该有的功能没有,花里胡哨的一大堆。如果你用SqlSugar, 会给你一个不错的选择, 不断给你惊喜。

自动分表 #

是ORM中唯一支持自动分表的ORM框架,.NET中并无相关框架,使用SqlSugar分表可以轻松处理产品历史数据, 日志,提高性能,对于上亿的流水数据分表是不错的选择

用法:https://www.donet5.com/Home/Doc?typeId=1201 (opens new window)

百万级写入 #

大数据处理最强解决方案,能支持百万级别的插入或者更新

用法: https://www.donet5.com/Home/Doc?typeId=2404 (opens new window)

基础性能 #

imageSqlSugarVsEfCore.rar (opens new window)sqlsugar对比efcore sqlserver 性能提升50%

imageSqlSugarVsEfCoreMySql.zip (opens new window)sqlsugar对比efcore mysql性能提高有2倍

SqlSugar VS Dapper 无论在性能还是在功能上都全面压制 Dapper ,虽然基础性能和Sugar打平手,但是批量操作 Dapper完全不行

SqlSugar VS EF ,SqlSugar小巧并且功能齐全,最重要的是上手容易,学习成本低 ,性能高于EF框架

每次查询10万条记录,2种模式都快于EFCore 4倍

image

根据主键查询,SqlSugarClient明显快(SqlSugarScope略快)

实战示例 #

1)插入 #

插入单条 #

//返回插入行数
db.Insertable(insertObj).ExecuteCommand(); //都是参数化实现
 
//插入返回自增列
db.Insertable(insertObj).ExecuteReturnIdentity();
 
//返回雪花ID 看文档3.0具体用法(在最底部)
long id= db.Insertable(实体).ExecuteReturnSnowflakeId();
 
//实用技巧1:获取数据库时间我们可以用   
var dbTime = db.GetDate();
//实用技巧2: 强制设置表名(默认表名来自实体) 
db.Insertable(insertObj).AS("table01").ExecuteCommand();

批量插入 #

//(1)、非参数化插入(防注入) 
//优点:综合性能比较平均,列少1万条也不慢,属于万金油写法,不加事务情况下部分库有失败回滚机质
//缺点:不支持【mysql表情emoji】和【Oracle类型clob4000+】 
db.Insertable(List<实体>).ExecuteCommand()  


​ //(2)、使用参数化内部分页插入 ​ //优点:500条以下速度最快,兼容所有类型和emoji ​ //缺点:500以上就开始慢了 (Oracle中500以上性能比第一种强),要加事务才能回滚 ​ db.Insertable(List<实体>).UseParameter().ExecuteCommand()//5.0.3.8-Preview及以上版本支持(NUGET搜索勾上包括预览)


​ //(3)、大数据写入(特色功能:大数据处理上比所有框架都要快30%) ​ //优点:1000条以上性能无敌手 ​ //缺点:API功能简单,如果用小数据量并发执行比上面的慢(异步可以解决),建议一次插入大量数据用这个 ​ //新功能 5.0.44 ​ db.Fastest<RealmAuctionDatum>().BulkCopy(GetList());//MySql连接字符串要加AllowLoadLocalInfile=true

根据datatable插入 #

//方案1
List<Dictionary<string,object>> dc= db.Utilities.DataTableToDictionaryList(dataTable);//5.0.23版本支持
db.Insertable(dc).AS("student").ExecuteReturnIdentity();
 
//方案2 :直接用datatable插入
DataTable datatable=xxxx;
dataTable.TableName = "order";//设置dt的name
db.Insertable(x11).UseSqlServer().ExecuteBulkCopy(); 
//.Use数据库 ,mysql需要配置连接字符串 AllowLoadLocalInfile=true

插入返回值 #

方法名 描述
ExecuteCommand 返回数据库受影响的行数,例如查询返回0,更新0条返回0,更新1条返回1
ExecuteReturnIdentity 返回自增列 (不支持批量返回自增)
ExecuteReturnBigIdentity 同上(不支持批量返回自增)
ExecuteReturnEntity 返回实体
ExecuteCommandIdentityIntoEntity 给传入实体添加自增列 (不支持批量)
ExecuteReturnSnowflakeId 返回雪花ID 看文档 3.1用法 5.0.3.5支持
ExecuteReturnSnowflakeIdList 返回雪花ID集合 文档 3.1用法 5.0.3.5支持

大数据插入 #

//插入 100万 数秒时间
db.Fastest<RealmAuctionDatum>().BulkCopy(GetList());//性能 比现有任何Bulkcopy都要快30%

雪花id #

  public class 实体
   {
        [SugarColumn(IsPrimaryKey =true)]//long类型的主键会自动赋值
        public long Id { get; set; }
  
        public string Name{get;set; }
   }
  long id= db.Insertable(实体).ExecuteReturnSnowflakeId();//单条插入返回雪花ID
  List<Long> ids = db.Insertable(List<实体>).ExecuteReturnSnowflakeIdList();//多条插入批量返回,比自增好用

2)更新 #

1.根据对象更新 db.Updateable(实体或者List<实体>) 优点: 代码少,支持各种批量修改 缺点: 1、不支持表达式和sql函数 2、依赖 实体对象 ,没实体对象就需要手动构造
2.根据表达式更新 db.Updateable<类名>() 优点: 1、需要更新什么写什么 2、支持SqlFunc函数也支持字段相加比如 Num=it.Num+1 缺点: 1、不支持批量更新不同记录,只支持批量更新统一的结果  2、没有实体更新节约代码
3.根据字典更新 db.Updateable(字典 或 List<字典>) 适合动态操作,没有实体操作,优缺点与 对象更新一样
3.1 根据DataTable更新 把DataTable转成字典 本质上还是字典更新

单条与批量 #

//根据主键更新单条 参数 Class
var result= db.Updateable(updateObj).ExecuteCommand();


​ //批量更新参数 List<Class> ​ var result= db.Updateable(updateObjs).ExecuteCommand();


​ //大数据批量更新 (MySql连接字符串要加AllowLoadLocalInfile=true ) ​ db.Fastest<RealmAuctionDatum>().BulkUpdate(GetList());


​ // 获取数据库时间 ,因没办法使用SqlFunc函数只能这么写 ​ var dbTime = db.GetDate();

函数 说明
ExecuteCommand 返回受影响行数 , update where 如果没找到那么就会返回 0
ExecuteCommandHasChange 返回bool ,等同于 bool isChange= ExecuteCommand()>0

1.2 不更新某列

不更新 TestId和CreateTime

var result=db.Updateable(updateObj).IgnoreColumns(it => new { it.CreateTime,it.TestId }).ExecuteCommand()

1.3 只更新某列

只更新 Name 和 CreateTime

var result=db.Updateable(updateObj).UpdateColumns(it => new { it.Name,it.CreateTime }).ExecuteCommand();

//UpdateColumnsIF 不能叠加,建议用表达式方式更新的SetColumnsIF

1.4 NULL列不更新

注意:不能支持批量 ,因为忽略null之后列数不一样,所以批量需要循环

db.Updateable(insertObj).IgnoreColumns(ignoreAllNullColumns:true).ExecuteCommand();

//更新忽略null字段

1.5 无主键/指定列

用法同上唯一区别就是用WhereColumns指定条件

var result= db.Updateable(updateObj).WhereColumns(it=>new { it.Id}).ExecuteCommand();//更新单 条根据ID
var result= db.Updateable(updateObjs).WhereColumns(it=>new { it.Id}).ExecuteCommand();//更新集合根据ID by id

可以多列

WhereColumns(it=>new { it.Id,it.Name}) //条件列不会被更新,只会作为条件

1.6 更新添加条件

注意:单条操作都支持

db.Updateable(updateObj).Where(it=>it.Id==1).ExecuteCommand()

//如果是集合操作请更新到5.0.4版本之前版本禁止使用, 并且只有部分库支持

大数据更新 #

//大数据更新,适合大数据更新,可以处理百万级
db.Fastest<RealmAuctionDatum>().BulkUpdate(GetList());//特色功能:吊打所有框架N倍,30列100万8秒更新完

根据表达式更新 #

根据表达式更新和上面的实体更新有着本质的区别,实体更新是要处理实体里面哪些字段要更新哪些不要更新

表达式则是需要更新什么写什么,哪种方式都可以就看你喜欢哪一种

2.1 指定多个字段更新

更新 name,createtime 条件id=11

var result= db.Updateable<Student>()
.SetColumns(it => new Student() { Name = "a", CreateTime = DateTime.Now })
.Where(it => it.Id == 11)
.ExecuteCommand();
//如果用实体更新为空的可能就更新了,表达式写2列更新2列

2.2 一个字段更新

只更新 name 条件id=1

var result= db.Updateable<Student>()
.SetColumns(it => it.Name == "jack")//SetColumns是可以叠加的 写2个就2个字段赋值
.Where(it => it.Id == 1)
.ExecuteCommand();
 
// Sql 
// Update Student  set Name='jack' where id=1 
 
//如果需要获取数据库时间我们可以用 SqlFunc.GetDate()

2.3 字段+1更新

//实现在原有字段+1
var result= db.Updateable<Student>()
.SetColumns(it => it.Num== it.Num+1)
.Where(it => it.Id == 1)
.ExecuteCommand();

2.4 Set语法是支持多个的

var result71 = db.Updateable<Order>()
               //生成 [name]=name
              .SetColumns(it => it.Name == it.Name)//加一个必须更新条件防止SETIF没有列
              //第一条件为true 生成 createtime=变量 
              .SetColumnsIF(p!=null ,it => it.CreateTime == p.Value)
               //第一条件为true 生成 X=变量 
              .SetColumnsIF(X!=null ,it => it.X== X)
            .Where(it => it.Id == 11).ExecuteCommand();

2.5 批量更新IN

var ids=new int[]{1,2,3};
var result71 = db.Updateable<Order>()
             .SetColumns(it => it.Name == "a")
            .Where(it => ids.Contains(it.Id)).ExecuteCommand();
            // in (1,2,3)

根据DataTable更新 #

List<Dictionary<string,object>> dc= db.Utilities.DataTableToDictionaryList(dataTable);//5.0.23版本支持
var t666 = db.Updateable(dc).AS("student").WhereColumns("id").ExecuteCommand();

3)插入或更新 #

简单使用 #

最低版本 5.0.3.4 ,判断新增还是修改,类似 merge into ,性能比merge into 好 不会死锁

var x = Db.Storageable(list2).ToStorage(); //将数据进行分组 
    x.AsInsertable.ExecuteCommand(); //执行插入 (可以换成雪花ID和自增)
    x.AsUpdateable.ExecuteCommand(); //执行更新 
//原理 
//x.AsInsertable 等于 Db.Insertable(x.InsertList.Select(it=>it.Item).ToList())
//x.AsUpdateable  等于  Db.Updateable(x.UpdateList.Select(it=>it.Item).ToList())


​ //老版本写法 如果插入更新都没数据用这个 ​ //var x = Db.Storageable(list2).Saveable().ToStorage()

大数据操作 #

对于性能要求高,数据量大的可以这么操作,适合1万以上数据处理

  var x= db.Storageable<Order>(data).ToStorage();
    x.BulkCopy();
    x.BulkUpdate(); //5.0.4.6
     
//winform写法需要注意 看db.Fastest文档

DataTable保存5.0.4.8 #

var dt=new DataTable();
dt.TableName = "order"; //设置表名
             
var addRow = dt.NewRow();
addRow["id"] = 0;
addRow["price"] = 1;
addRow["Name"] = "a";
dt.Rows.Add(addRow);//添加娄据
             
var x= db.Storageable(dt).WhereColumns("id").ToStorage();
x.AsInsertable.IgnoreColumns("id").ExecuteCommand();//如果是自增要添加IgnoreColumns
x.AsUpdateable.ExecuteCommand();

4)事务 #

1、单库事务 #

单库事务是针一个db操作执行的事务,无论是 ISqlSugarClient和 SqlSugarClient 用法都一样

    try
    {
        db.Ado.BeginTran();
        db.Insertable(new Order() { .....}).ExecuteCommand();
        db.Ado.CommitTran();
    }
    catch (Exception ex)
    {
        db.Ado.RollbackTran();
        throw ex;
    }

如果一个db就一个库,那么你也可以用多租户事务节约代码,因为2者在一个库的情况下作用一样

db.BeginTran();//去掉了.ado
db.CommitTran();//去掉了.ado
db.RollbackTran();//去掉了.ado
//ISqlSugarClient 接口使用多租户事务 看文档2.2

2、多租户事务 #

多数据库事务是SqlSugar独有的功能,稳定比CAP更强(CAP还有一层队列),在单个程序中可以很愉快的使用多库事务

SqlSugarClient或者SqlSugarSope 继承于2个接口 ,代码如下事物

SqlSugarClient : ISqlSugarClient, ITenant

多租户声明

SqlSugarClient db = new SqlSugarClient(new List<ConnectionConfig>(){
new ConnectionConfig(){ ConfigId="0", DbType=DbType.SqlServer,ConnectionString=..,IsAutoCloseConnection=true},
new ConnectionConfig(){ ConfigId="1", DbType=DbType.MySql,ConnectionString=..,IsAutoCloseConnection=true}
});

简单的说多租户事务和单库事务用法基本100%一致,唯一区别就是少了.Ado

 db.Ado.BeginTran//单库
 db.BeginTran //多库

2.1 SqlSugarClient事务 #

因为继承 ITenant 了可以直接使用   (db.GetConnection要写在事务外面)

 //单库事务为 db.Ado.BeginTran,多租户是db.BeginTran
 try
 {
     var mysqldb = db.GetConnection("1");//获取ConfigId为1的数据库对象
     var sqlServerdb = db.GetConnection("0");//获取默认对象  
     db.BeginTran();
      
     sqlServerdb.Insertable(new Order() { }).ExecuteCommand();
     mysqldb.Insertable(new Order() { }).ExecuteCommand();
      
     db.CommitTran();
 }
 catch (Exception)
 {
     db.RollbackTran();//数据回滚
      throw;
 }

2.2 ISqlSugarClient事务 #

因为和ITenant没有继承关需要转换一下

db.AsTenant().BeginTran();//低版本 (db as ITenant).BeginTran() 
db.AsTenant().CommitTran();
db.AsTenant().RollbackTran();

4、跨方法事务 #

3.1 我们可以用 SqlSugar.Ioc实现 Db在一个 上下文共享 db对象(支持异步),实现跨方法事务

点击查看   WebApi注入-SqlSugar.IOC-文档园 (donet5.com) (opens new window)

3.2 我们可以用 SqlSugarScope 单例模式 来实现事务跨方法

工作单元:https://www.donet5.com/Home/Doc?typeId=2360 (opens new window)

5、CAP事务 #

CAP可以支持跨程序间的事务处理(非跨程序事务不建议用,涉及到队列等,在单程序中稳定性肯定不如自带的多租户事务)

注意: MySql用户使用  sqlSugarCore.MySqlConnector  替换 sqlSugarCore , Cap2.6以上只能用MySqlConnector

1、数据库的自动释放要关闭

2、手动打开数据库连接 db.Ado.Connection.Open();

3、用db.Ado.Connection创建事务

4、把你的事务赋值到ORM对象 db.Ado.Transaction = 你的事务;

5、执行你的代码

6、关闭Connection对象

//用户使用案例
var db=GetSqlsugarclient();//关闭自动释放
using (var connection = (MySqlConnection)db.Ado.Connection)
{
     using (var transaction = connection.BeginTransaction(_capBus, autoCommit: false))
 
     {
         if (connection.State != ConnectionState.Open)
         {
            connection.Open();
         }
 
          db.Ado.Transaction = (IDbTransaction)transaction.DbTransaction;//这行很重要
                  
          db.Insertable<Test>(new Test()
          {
            name = DateTime.Now.ToString()
          }).ExecuteCommand();
 
          _capBus.Publish("Sample.RabbitMQ.MySql", DateTime.Now);
 
         transaction.Commit();
 
       }
}

6、异步事务 #

请不要在同步方法里面写下面方代码,必须是异步方法才行 返回是要带有Task async

用法1:

  try
     {
           db.BeginTran();
 
                await db.Insertable(new Order()
                {
                    CreateTime = DateTime.Now,
                    CustomId = 1,
                    Name = "aaa",
                    Price = 0
                }).ExecuteCommandAsync();
 
            db.CommitTran();
       }
       catch (Exception)
       {
             db.CommitTran();
        }

用法2:

  //只有5.0.3.8支持,老版本请升级使用
  var res = await db.UseTranAsync(async () =>
  {
      await db.Insertable(new Order()
      {
          CreateTime = DateTime.Now,
          CustomId = 1,
          Name = "aaa",
          Price = 0
       }).ExecuteCommandAsync();
          
   });
    if (res.IsSuccess)
    {
 
    }

7、设置事务隔离级别 #

单库模式用法

try
{
    db.Ado.BeginTran(IsolationLevel.ReadCommitted);
         
        //业务代码  
         
    db.Ado.CommitTran();
}
catch (Exception ex)
{
    db.RollbackTran();
    throw ex;
}

多租户模式

 var mysqlDb = db.GetConnection("mysql");
 var mssqlDb = db.GetConnection("mssql");
 try
 {
        mysqlDb.Ado.BeginTran(IsolationLevel.ReadCommitted);//开启库1的事务
        mssqlDb.Ado.BeginTran(IsolationLevel.ReadCommitted);//开启库2的事务
                 
         //业务代码 只能用  mysqlDb和 mssqlDb 
 
        db.CommitTran();//注意不能用db.ado.CommitTran
 }
 catch (Exception ex)
 {
        db.RollbackTran();
        throw ex;
}

5)查询导航 #

基础查询

基础查询 查询单条、主键查询、查所有、模糊查询、排序、TOP 、Count、查单条、IN等等操作 查看 (opens new window)
分页查询 分页查询 查看 (opens new window)
分组查询 分组查询和去重复 Group by Distinct 查看 (opens new window)
排序 Order by 、随机排序、动态排序 查看 (opens new window)

多表查询

联表查询 使用Left Join Inner Join进行查询 查看 (opens new window)
配置查询 简化联表操作,解决字典联表和简单Name联表问题 查看 (opens new window)
子查询 子查询 查看 (opens new window)
嵌套查询 嵌套联表查询、多合一嵌套、一合一嵌套 查看 (opens new window)
导航查询 一对多 、一对一、多对多操作 ,有层级的查询 查看 (opens new window)
并集查询 Union all 查看 (opens new window)
树型查询 查询出一个树形结构,比如菜单 查看 (opens new window)

业务查询

无实体查询 没有实体查询 查看 (opens new window)
表格查询 前端组装好查询条件,后台直接使用 查看 (opens new window)
全局过滤器 比如很多地方用到假删除,那么我们可以配置加上IsDeleted 查看 (opens new window)
多库查询 如果表结构一样,那我们可以用一个实体操作不同表 查看 (opens new window)

高级功能

二级缓存 支持Redis等缓存,让你不需要维护 CacheKey轻松使用缓存来提高服务器性能 查看 (opens new window)
异步查询 使用异步进行查询 查看 (opens new window)
Sqlfun函数 使用SqlSugar自带的数据库函数查询 查看 (opens new window)
扩展Sql函数 当有些ORM不能解析的功能,可以自已封装SQL函数 查看 (opens new window)
动态表达式 动态创建表达式,解析表达式成SQL 查看 (opens new window)
报表查询 集合和表的Join , 报表统计 查看 (opens new window)

Queryable

Select用法 一列、多列、匿名对象、多表映射等 查看 (opens new window)
Where用法 表达式、拼表达式、Sql、动态条件 等 查看 (opens new window)
生命周期 原理、引用类型、拷贝机制 查看 (opens new window)
执行查询 ToList First ToDateTable ToJson ToTree ToParentList ToSql ToPivotList ToPivotTable ToClassString ToDictionary ToDictionaryList Count Any Sum Max Min 查看 (opens new window)

分页查询 #

 int pageIndex = 1; // pageindex是从1开始的不是从零开始的
 int pageSize = 20;
 int totalCount=0;
 //单表分页
 var page = db.Queryable<Student>().ToPageList(pageIndex, pageSize, ref totalCount);
 //如果SqlServer不想有Rownumber可以用 ToOffsetPage 较新版本支持


​ //多表分页 ​ var list = db.Queryable<Student, School>((st,sc)=>new JoinQueryInfos(JoinType.Left,st.SchoolId==sc.Id)) ​ .Select((st,sc)=>new{Id=st.Id,Name=st.Name,SchoolName=sc.Name}) ​ .ToPageList(pageIndex, pageSize, ref totalCount); ​ public int RowIndex{get;set;} //行号 序号 ​
​ db.Queryable<Student>().ToPageList(pageIndex, pageSize, ref totalCount) ​
​ //其他数据库可以这么实现 ​ int i = 1; ​ var getAll = db.Queryable<Order>().Mapper((it,cache)=> { ​ it.num= i;//有分页的话需要计算一下 (pageindex-1)*pagesize+i ​ i++; ​ }).ToList();

基础查询功能 #

查所有

List<Student> list=db.Queryable<Student>().ToList()
//select * from Student

按条件查询

db.Queryable<Student>().Where(it=>it.Id==1).ToList()
//select * from Student where id=1

多条件查询

db.Queryable<Student>().Where(it=>it.Id>10&&it.Name=="a").ToList()
//select * from Student where id>10 and name='a'
db.Queryable<Student>().Where(it=>it.Id>10).Where(it=>it.Name=="a").ToList()
//select * from Student where id>10 and name='a'
//如果是或者关系可以用 ||

动态OR查询

var exp= Expressionable.Create<Student>();
exp.OrIF(条件,it=>it.Id==1);//.OrIf 是条件成立才会拼接OR
exp.Or(it =>it.Name.Contains("jack"));//拼接OR
var list=db.Queryable<Student>().Where(exp.ToExpression()).ToList();

模糊查询

db.Queryable<Student>().Where(it =>it.Name.Contains("jack")).ToList();
//select  * from  Student where name like %jack%

根据主键查询

susgar中的single等同于EF中的SingleOrDefault

db.Queryable<Student>().InSingle(2) //通过主键查询 SingleById
db.Queryable<Student>().Single(it=>it.Id==2) //根据ID查询
//select * from Student where id=2

查询第一条

.First() 等同于C#中的 FirstOrDefault , 没有值返回 null

db.Queryable<Student>().First(it=>it.Id==1) //没有返回Null
//select top 1 * from Student where id=1

查前几条

db.Queryable<Student>().Take(10).ToList()
//select top 10 * from Student

数据行数

db.Queryable<Student>().Where(it=>it.Id>11).Count()//同步
db.Queryable<Student>().Where(it=>it.Id>11).CountAsync()//异步
//select count(*) from Student where id>11
//你也可以用函数
SqlFunc.AggregateCount

设置新表名

//例1:更新表名
db.Queryable<School>().AS("Student").ToList();
//生成的SQL  SELECT [ID],[NAME] FROM  Student
//动态表名 表别名 指定表明


​ //例2:给表名添加前缀 ​ db.Queryable<School>().AS("dbo.School").ToList(); ​ //生成的SQL SELECT [ID],[NAME] FROM dbo.School


​ //如果不用AS也可以在特性中设置别名,看文档:实体本置

是否存在记录

db.Queryable<Student>().Where(it=>it.Id>11).Any()
db.Queryable<Student>().Any(it=>it.Id>11) //上面语法的简化
//异步就是 AnyAsync()

In查询,IN的使用

单个字段

int [] allIds =new int[]{2,3,31};
db.Queryable<OrderItem>().Where(it => allIds.Contains(it.OrderId)).ToList()
//orderid in (2,3,31)

多个字段

 List<Order> OrderList= Pars;
 Expressionable<Order> exp = new Expressionable<Order>();
 foreach (var item in OrderList)
 {
   exp.Or(it => it.Id == item.Id && it.Name==item.Name);
 }
//使用构造好的表达式
var list = db.Queryable<Order>().Where(exp.ToExpression()).ToList();
 
//SELECT [Id],[Name],[Price],[CreateTime],[CustomId] FROM [Order]  WHERE
 //((((( [Id] = @Id0 ) AND ( [Name] = @Name1 )) OR
  //(( [Id] = @Id2 ) AND ( [Name] = @Name3 ))) OR 
  //(( [Id] = @Id4 ) AND ( [Name] = @Name5 ))) OR 
  //(( [Id] = @Id6 ) AND ( [Name] = @Name7 )))

使用 in 的模糊查询

var names= new string [] { "a","b"};
Expressionable<Order> exp = new Expressionable<Order>();
foreach (var item in names)
{
    exp.Or(it => it.Name.Contains(item));
}
var list= db.Queryable<Order>().Where(exp.ToExpression()).ToList();
//  where ( name like xx1 or name like xx2 or name like xx3)

NOT IN

int [] allIds =new int[]{2,3,31};
db.Queryable<OrderItem>().Where(it => !allIds.Contains(it.OrderId)).ToList()
//orderid  NOT in (2,3,31)

简单排序

db.Queryable<Student>().OrderBy((st,sc)=>sc.Id,OrderByType.Desc).ToList()
//排序 可以多个

查询一列

db.Queryable<Student>().Select(it=>it.Name).ToList() //单值 查询列 查询单独列

查询单条

查询一条

db.Queryable<Student>().First(it=>it.Id==1) //没有返回Null,如果结果大于1条会抛出错误
//select * from Student where id=1 // 查询id等于1的单条记录

获取最大值

db.Queryable<Order>().Max(it=>it.Id);//同步 
db.Queryable<Order>().MaxAsync(it=>it.Id);//异步
//也可以用函数 SqlFunc.AggregateMax

获取最小值

db.Queryable<Order>().Min(it=>it.Id);//同步
db.Queryable<Order>().MinAsync(it=>it.Id);//异步
//也可以用函数 SqlFunc.AggregateMin

求和

db.Queryable<Order>().Sum(it=>it.Id);//同步
db.Queryable<Order>().SumAsync(it=>it.Id);//异步
//也可以用函数 SqlFunc.AggregateSum

6)删除 #

1、根据实体删除 #

需要配置主键 ,根据主键删除需要给实体配置主键,参考文档实体配置

//单个实体
db.Deleteable<Student>().Where(new Student() { Id = 1 }).ExecuteCommand();
 
//List<实体>
List<Student> list=new List<Student>(){
  new Student() { Id = 1 }
};
db.Deleteable<Student>(list).ExecuteCommand(); //批量删除
函数 说明
ExecuteCommand 返回受影响行数 , update where 如果没找到那么就会返回 0
ExecuteCommandHasChange 返回bool ,等同于 bool isChange= ExecuteCommand()>0

2、根据主键 #

db.Deleteable<Student>().In(1).ExecuteCommand();
 
//无主键用法
 db.Deleteable<Order>().In(it=>it.Id,1).ExecuteCommand();

3、根据主键数组 #

db.Deleteable<Student>().In(new int[] { 1, 2 }).ExecuteCommand();
 
//无主键用法
db.Deleteable<Student>().In(it=>it.Id,new int[] { 1, 2 }).ExecuteCommand();
 
//Oracle用户注意:这种方式只能删除1000,用达式方式.Where(it=>ids.Contains(it.Id)支持1000以上

4、根据表达式 #

db.Deleteable<Student>().Where(it => it.Id == 1).ExecuteCommand();

5、子查询删除 #

db.Deleteable<Student>()
             .Where(p => SqlFunc.Subqueryable<School>().Where(s => s.Id == p.SchoolId).Any())
             .ExecuteCommand()
//.Where(s => s.Id == p.SchoolId)  s和p的关联条件不能少,不然就全删了

6、无实体删除 #

db.Deleteable<object>().AS("[Order]").Where("id=@id",new { id=1}).ExecuteCommand();
db.Deleteable<object>().AS("[Order]").Where("id in (@id) ",new { id=new int[]{1,2,3}}).ExecuteCommand();//批量

7、全局过滤器(5.0.4.1) #

//配置表过滤器
db.QueryFilter.Add(new TableFilterItem<Order>(it => it.Name.Contains("a")));
 
//查询有效 
db.Queryable<Order>().ToList();
//SELECT [Id],[Name],[Price],[CreateTime],[CustomId] FROM [Order]  WHERE  ([Name] like '%'+@MethodConst0+'%') 
 
//删除也有效
db.Deleteable<Order>().EnableQueryFilter().Where(it=>it.Id==1).ExecuteCommand();
//DELETE FROM [Order] WHERE  ([Name] like '%'+@MethodConst1000+'%')  AND ( [Id] = @Id0 )

8、逻辑删除(5.0.4.3) #

请升级到5.0.4.4,(5.0.4.3存在BUG)

实体属性有isdelete或者isdeleted

 db.Deleteable<LogicTest>().In(100).IsLogic().ExecuteCommand(); //假删除 软删除

指定属性

 db.Deleteable<LogicTest>().In(100).IsLogic().ExecuteCommand("mydelete");

说明:

用法与删除一样唯一多了个.IsLogic()

7)仓储模式 #

1、仓储说明 #

仓储可以让你的方法更加的规范,需要什么方法都封装到仓储中,下次就能重复使用,并且能很好的和你业务拆分开

这种设计模式简单粗暴用起来也方便 ,文章下面有可以运行的DEMO

2、仓储方法 #

仓储有一套自带的数据库操作方法,比起 db.xx.xxx来说可能更简便些满足一些常用需求, 复杂的功能还是用db.xxx.xxx

//查询
var data1 = base.GetById(1);//根据id查询
var data2 = base.GetList();//查询所有
var data3 = base.GetList(it => it.Id == 1); //根据条件查询
var data4 = base.GetSingle(it => it.Id == 1);//查询单条记录,结果集不能超过1,不然会提示错误
var  data= base.GetFirst(it => it.Id == 1);//查询第一条记录
var p = new PageModel() { PageIndex = 1, PageSize = 2 };
var data5 = base.GetPageList(it => it.Name == "xx", p);
Console.Write(p.PageCount);
var data6 = base.GetPageList(it => it.Name == "xx", p, it => it.Name, OrderByType.Asc);
Console.Write(p.PageCount);
List<IConditionalModel> conModels = new List<IConditionalModel>();
conModels.Add(new ConditionalModel(){FieldName="id",ConditionalType=ConditionalType.Equal,FieldValue="1"});//id=1
var data7 = base.GetPageList(conModels, p, it => it.Name, OrderByType.Asc);
base.AsQueryable().Where(x => x.Id == 1).ToList();
 
//插入
base.Insert(insertObj);
base.InsertRange(InsertObjs);
var id = base.InsertReturnIdentity(insertObj);
base.AsInsertable(insertObj).ExecuteCommand();


​ //删除 ​ base.Delete(insertObj); ​ base.DeleteById(1); ​ base.DeleteByIds(new object [] { 1, 2 }); //数组带是 ids方法 ,封装传 object [] 类型 ​ base.Delete(it => it.Id == 1); ​ base.AsDeleteable().Where(it => it.Id == 1).ExecuteCommand(); ​
​ //更新 ​ base.Update(insertObj); ​ base.UpdateRange(InsertObjs); ​ base.Update(it => new Order() { Name = "a", }, it => it.Id == 1); ​ base.AsUpdateable(insertObj).UpdateColumns(it=>new { it.Name }).ExecuteCommand();


​ //高级操作 ​ base.AsSugarClient // 获取完整的db对象 ​ base.AsTenant // 获取多库相关操作 ​
​ //切换仓储 ​ base.ChangeRepository<Repository<OrderItem>>() //支持多租户和扩展方法,使用SqlSugarScope单例(或者SqlSugarClient Scope注入) ​ base.Change<OrderItem>()//只支持自带方法和单库

3、创建仓储 #

只需要几行代码就搞定了,我们定义的Repository是公用类,不能包含具体的类务逻辑,即使不使用扩展方法自带的方法也够开发

public class Repository<T> : SimpleClient<T> where T : class, new()
{
    public Repository(ISqlSugarClient context = null) : base(context)//注意这里要有默认值等于null
    {
         
         base.Context=context;//ioc注入的对象
        // base.Context=DbScoped.SugarScope; SqlSugar.Ioc这样写
        // base.Context=DbHelper.GetDbInstance()当然也可以手动去赋值
    }
 
    /// <summary>
    /// 扩展方法,自带方法不能满足的时候可以添加新方法
    /// </summary>
    /// <returns></returns>
    public List<T> CommQuery(string json)
    {
       //base.Context.Queryable<T>().ToList();可以拿到SqlSugarClient 做复杂操作
       return null;
    }
     
}

注意:public Repository(ISqlSugarClient context = null) 默认值不能少,不然无参方式 IOC 不好注入

4、使用仓储 #

继承的时候指定类型为Order,那么OrderService的所有操作都是针对Order表的

//订单服务
public class OrderService: Repository<Order>  
{
      //业务方法
    public  Order GetOrderByName(string name)
    {
      return base.GetSingle(it=>it.Name==name); //GetSingle是仓储自带的方法,本文最上方有详细介绍
    }
}
 
//Order.cs文件
public class Order
{
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
 
}

5、调用外部仓储 #

当继承了Repository<Order>就能使用仓储里面的方法,但只是针对Order表的操作,可我还想使用OrderItem这个仓储怎么办?

用法如下:

 public class OrderService : Repository<Order>
 {
        public List<OrderItem> GetOrderItems()
        {
             //切换仓储   请升级到5.0.2.9+
             var orderItemDb = base.ChangeRepository<Repository<OrderItem>>();;
             //base.Change已过期 不支持多库和自定义方法
              return orderItemDb.GetList();
        }
 
        public List<Order> GetOrders()
        {
            return base.GetList(); //使用自已的仓储方法
        }
 
        public List<Custom> GetCustom()
        {
            return base.Context.Queryable<Custom>().ToList(); //使用完整SqlSugar
        }
 }

6、仓储中使用事务 #

  try
    {
        base.AsTenant().BeginTran();
 
        //你的增查改方法
 
         base.AsTenant().CommitTran();
     }
     catch (Exception ex)
     {
        base.AsTenant().RollbackTran();                  
        throw;
     }

7、多租户使用仓储 #

因为要多个仓储用一个SqlSugarClient,我们需要用到Sugar.Ioc进行DB共享

https://www.donet5.com/Home/Doc?typeId=2405 (opens new window)

8)工作单元模式/IUnitOfWorK/DbContext #

简单用例 #

SqlSugarUnitOfWork 原理:结合仓储方法加SqlSugarScope单例模式 实现了一个工作单元模式

语法糖1:

public class Test1Manager:DbContext
{
    //使用DbContext
    public void Test(List<Order> orderList,List<Custom> customList, List<XXX> XX)
    {
 
       Db.UseTran(() =>
       {
            //调用外部方法也支持事务
            new Test2Manager().Insert(XX);//Test2Manager也是继承DbContext
             
            //插入数据
            CustomDal.Insert(customList);//使用Custom仓储方法
            OrderDal.Insert(orderList);//使用Order仓储方法
 
            throw new Exception("故意出错"); //出错会自动回滚
        },
        e =>
        {
        //异常事件 
        //throw e;
        });
 
    }
}

语法糖2:(5.0.4才支持)

public class Test1Manager:DbContext
{
    //使用DbContext
    public void Test(List<Order> orderList,List<Custom> customList, List<XXX> XX)
    {
 
         using (var tran = db.UseTran())
         {
          
               //业务代码
               new Test2Manager().Insert(XX);
               OrderDal.Insert(orderList)
               .....出错会自动回滚
                ....
                         
             tran.CommitTran();//这个提交不能漏掉
         }
 
    }
}

创建DbContext #

//创建 DbContext
public class DbContext
{
    //SqlSugarScope单例模式,支持跨方法事务线程安全
    public static SqlSugarScope Db = new SqlSugarScope(new ConnectionConfig()
    {
        DbType = SqlSugar.DbType.SqlServer,
        ConnectionString = Config.ConnectionString,
        IsAutoCloseConnection = true
    },
    db => {
        //单例参数配置,所有上下文生效
        db.Aop.OnLogExecuting = (s, p) =>
        {
            Console.WriteLine(s);
        };
    });


​ public static ISimpleClient<Order> OrderDal => Db.GetSimpleClient<Order>();//创建仓储方法 ​ public static ISimpleClient<Custom> CustomDal => Db.GetSimpleClient<Custom>();//创建仓储方法 ​
​ }

扩展DbContext #

public static ISimpleClient<Order> OrderDal => Db.GetSimpleClient<Order>() 自带的仓储方法满中不了

代码如下:

public class DbContext  
{
    public static SqlSugarScope Db = new SqlSugarScope(new ConnectionConfig()
    {
        DbType = SqlSugar.DbType.SqlServer,
        ConnectionString = Config.ConnectionString,
        IsAutoCloseConnection = true
    },db =>{
         //单例参数配置,所有上下文生效
         db.Aop.OnLogExecuting = (s, p) =>
         {
            Console.WriteLine(s);
         };
     });
 
    public static DbSet<Order> OrderDal => new DbSet<Order>();
    public static DbSet<Custom> CustomDal =>new DbSet<Custom>();
         
      //扩展出来的仓储 取名叫DbSet      
    public class DbSet<T> : SimpleClient<T> where T : class, new()
    {
        public DbSet(ISqlSugarClient context = null) : base(context)//需要有构造参数
        {
            base.Context = DbContext.Db;
        }
 
        /// <summary>
        /// 扩展方法,自带方法不能满足的时候可以添加新方法
        /// </summary>
        /// <returns></returns>
        public List<T> CommQuery(string json)
        {
            //base.Context.Queryable<T>().ToList();可以拿到SqlSugarClient 做复杂操作
            return null;
        }
 
    }
}

仓储方法所有API #

查看文档仓储模式:https://www.donet5.com/Home/Doc?typeId=1228 (opens new window)

9)多租户 #

多租户优点 #

如果你手动new 多个 db对象 想要维护起来将会很不方便,特别是事务

多租户让你的代码更加简洁并且支持多库事务

接口使用说明 #

ISqlSugarClient和SqlSugarClient不同,ISqlSugarClient不包含租户方法,原因如下

SqlSugarClient : ISqlSugarClient, ITenant //ISqlSugarClient和ITenant是平行关系,没有租户方法

我们可以通过自带转换实现

ISqlSugarClient db= 注入db ;
db.AsTenant().BeginTran();
db.AsTenant().CommitTran();
db.AsTenant().RollbackTran();
db.AsTenant().GetConnection(1)
db.AsTenant().IsAnyConnection(1)
//低版本 (db as ITenant).BeginTran()

1、 创建多库 #

添加多库的方式分为2种

1.1 使用Add方式动态添加Db #

var db = new SqlSugarClient(new ConnectionConfig(){
                   DbType = SqlSugar.DbType.SqlServer,
                   IsAutoCloseConnection = true,
                   ConnectionString = Config.ConnectionString1,
                   ConfigId = "0"});//可以配一个默认
  //动态添加连接
  db.AddConnection(new ConnectionConfig(){
                DbType = SqlSugar.DbType.SqlServer,
                ConfigId = "1",//设置库的唯一标识
                IsAutoCloseConnection = true,
                ConnectionString = Config.ConnectionString2 });
                 
  //db.IsAnyConnection(configId)可以验证有没有当前连接

1.2 一次性New多个db #

var db = new SqlSugarClient(new List<ConnectionConfig>()
{
 new ConnectionConfig(){ConfigId="0",DbType=DbType.SqlServer,ConnectionString=..,IsAutoCloseConnection=true},
 new ConnectionConfig(){ConfigId="1",DbType=DbType.MySql,ConnectionString=..,IsAutoCloseConnection=true  }
});

1.3 上面2种混合使用 #

当然也支持定义多个,然后在Add动态添加,非常灵活

2、如何使用多库 #

当定义好多库后,我们代码使用多库操作 也有2种情况

2.1 通过获取子对象 #

var mysqldb = db.GetConnection("1");//获取config为1的数据库对象
var sqlServerdb = db.GetConnection("0");//获取默认对象
mysqldb.Queryable<Order>().ToList();
sqlServerdb.Queryable<Order>().ToList();

2.2 通过切换数据库 #

//使用默认数据库对象
db.Deleteable<Order>().ExecuteCommand();
 
//切换数据库 ConfigId = 1
db.ChangeDatabase("1"); //改变db.的默认数据库
 
db.Deleteable<Order>().ExecuteCommand();

2.3 动态获取 ConfigId #

上面2种方式可以通过实体配置做到动态获取租户ID,不需要手写

[TenantAttribute("1")]
  public class C1Table
  {
     public string Id { get; set; }
  }
  
  [TenantAttribute("2")]
  public class C2Table
  {
      public string Id { get; set; }
  }
   
  var configId = typeof(C1Table).GetCustomAttribute<TenantAttribute>().configId;//动态获取configid
  //通过实体动态获取用例 https://www.donet5.com/Doc/10/2254

3、多库事务 #

需要注意的是事务方法和逻辑方法都是基于一个  db (说明:db是SqlSugarClient对象 ) 才会有效

//下面2行代码要写在事务开启之前
var mysqldb = db.GetConnection("1");//获取ConfigId为1的数据库对象
var sqlServerdb = db.GetConnection("0");//获取默认对象
 
//开启事务 (不能是db.ado.BeginTran)
db.BeginTran();
            mysqldb.Insertable(new Order()
            {
                CreateTime = DateTime.Now,
                CustomId = 1,
                Name = "a",
                Price = 1
            }).ExecuteCommand();
            mysqldb.Queryable<Order>().ToList();
            sqlServerdb.Queryable<Order>().ToList();
//提交事务  (不能是db.ado.CommitTran)
db.CommitTran();

其中mysqldb和sqlserverdb都是db创建的所以在同一个上下文

注意:db.GetConnection 不能写在事务里面,如果想将db.GetConnection写在事务里面

需要将db.GetConnection 在事务外面先执行一下,下次在事务里面用GetConnection就可以了

学习事务: 数据事务-SqlSugar 5x-文档园 (donet5.com) (opens new window)

4、多租户设置AOP #

AOP在多租户是不共享的,需要单独设置,满足更多需求,你可以循环添加
 db.GetConnection("1").Aop.OnLogExecuting = (sql, pars) =>
 {
     Console.WriteLine("执行1库"+sql);
 };
 db.GetConnection("0").Aop.OnLogExecuting = (sql, pars) =>
 {
     Console.WriteLine("执行0库"+sql);
 };

5、默认值 #

默认db操作的是第一个库

SqlSugarClient db = new SqlSugarClient(new List<ConnectionConfig>()
{
    new ConnectionConfig(){ ConfigId="0", DbType=DbType.SqlServer, 
    ConnectionString=Config.ConnectionString,InitKeyType=InitKeyType.Attribute,IsAutoCloseConnection=true },
    new ConnectionConfig(){ ConfigId="1", DbType=DbType.MySql, 
    ConnectionString=Config.ConnectionString4 ,InitKeyType=InitKeyType.Attribute ,IsAutoCloseConnection=true}
});

6、对表进行过滤 #

如果要对表进行数据隔离可以看 查询过滤器的例子

https://www.donet5.com/Home/Doc?typeId=1205 (opens new window)

上次更新: 3/8/2023, 4:23:32 PM