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)
基础性能 #
SqlSugarVsEfCore.rar (opens new window)sqlsugar对比efcore sqlserver 性能提升50%
SqlSugarVsEfCoreMySql.zip (opens new window)sqlsugar对比efcore mysql性能提高有2倍
SqlSugar VS Dapper 无论在性能还是在功能上都全面压制 Dapper ,虽然基础性能和Sugar打平手,但是批量操作 Dapper完全不行
SqlSugar VS EF ,SqlSugar小巧并且功能齐全,最重要的是上手容易,学习成本低 ,性能高于EF框架
每次查询10万条记录,2种模式都快于EFCore 4倍
根据主键查询,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)