本文demo适用于MySQL

Dapper优势和缺点

优点

高性能、易排查、易运维、灵活可控

缺点

和EF相比,手写sql当修改表结构不易发现bug。

习惯了EF后再来使用Dapper,会很难适应那种没有了强类型的安全感。不过可以用单元测和心细来避免。

数据库连接

问题:IDbConnection需不需要手动Open打开连接

答案:有时候需要有时候不需要

Dapper连接可分两种:主动管理(自己管理连接的打开和关闭)和自动管理(自动管理连接的打开和关闭)

//短短三行代码即实现了dapper连接的主动管理和自动管理

bool wasClosed = cnn.State == ConnectionState.Closed;//判断连接是否为关闭状态
...
if (wasClosed) cnn.Open();
...
if (wasClosed) cnn.Close();

源码位置 https://github.com/StackExchange/Dapper/blob/master/Dapper/SqlMapper.cs#L530

Note:ADO.NET默认是启用连接池的 Pooling = true,连接池中最大连接数,默认为100

在使用Dapper的过程中,你有可能遇到过连接池超过最大限制。那问题是怎么来的呢?

如果主动管理或者自动管理连接都不会有问题。就怕你管理一半,打开不关闭:

//循环执行两百次左右就可以重现连接池超过最大限制
DBContext dBContext2 = new DBContext();
dBContext2.DbConnection.Open();

解决办法相信不用我说了。

Note:在使用事务的时候需要手动打开连接,请不要忘记在finally里面Close。

增删改查的优化

批量新增

//1、可通过匿名对象集合进行参数化数据新增。(性能优化参考3)
DbConnection.Execute(sqlStr, ListEntity);
//2、【sql拼接可大大优化执行效率】在values后面带上多有要插入的值。(如果数据太大可分批插入,如1000条一提交)
insert into tt (a,b,c,d) values (50,1,'1','1'), (51,2,'1','2');
//3、参数化防sql注入
var sql = insert into tt (a,b,c,d) values (@a1,@b1,@c1,@d1), (@a2,@b2,@c2,@d2);
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("a1","value");
dynamicParameters.Add("b1","value");
dynamicParameters.Add("c1","value");
dynamicParameters.Add("a2","value");
dynamicParameters.Add("b2","value");
dynamicParameters.Add("c2","value");
dynamicParameters.Add("d2","value");
DbConnection.ExecuteScalar<int>(sql, dynamicParameters)

批量修改

//1、可通过匿名对象集合进行参数化数据修改。(需要修改的值都不一样的情况下,性能优化参考4)
DbConnection.Execute(sqlStr, ListEntity);
//2、如果需要修改的值都是一样,只是条件不一样。(使用SQL语句中的IN语法)
DbConnection.Execute("UPDATE tt SET aa = @aa where bb in @bb;", new { aa, bb });
//3、快速批量修改(此方法非常适合`新增或修改`数据的场景,可通过建联合唯一索引来实现新增或修改的区分。【组合字段不能为空,否则为空 不做唯一,有重复空数据】)
insert into test_tbl (id,dr) values (1,'2'),(2,'3'),...(x,'y') on duplicate key update dr=values(dr);
//4、参数化防sql注入
var sql = insert into test_tbl (id,dr) values (@id1,@dr1),(@id2,@dr2),...(@idn,@drn) on duplicate key update dr=values(dr);
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("id1","value");
dynamicParameters.Add("dr1","value");
dynamicParameters.Add("id2","value");
dynamicParameters.Add("dr2","value");
...
dynamicParameters.Add("idn","value");
dynamicParameters.Add("drn","value");
DbConnection.ExecuteScalar<int>(sql, dynamicParameters)

批量删除

同理,也可以使用参数化和IN语法

查询第一条数据

dBContext.DbConnection.QueryFirstOrDefault<ItemFCLPO>("SELECT * from itemfcl_temp limit 1;");    //正确
dBContext.DbConnection.QueryFirstOrDefault<ItemFCLPO>("SELECT * from itemfcl_temp;"); //错误
dBContext.DbConnection.Query<ItemFCLPO>("SELECT * from itemfcl_temp;").FirstOrDefault(); //错误
dBContext.DbConnection.Query<ItemFCLPO>("SELECT * from itemfcl_temp;").ToList().FirstOrDefault();//错误

If扩展方法

使用过Mybatis的同学都知道,在xml里面写if、else还是蛮好用的。虽然我还是不喜欢在xml里面写sql。

那么在Dapper里面是不是也能简便操作,答案是肯定的。这就得庆幸C#牛逼的语法了。

public static class StringExtension
{
public static string If(this string str, bool condition)
{
return condition ? str : string.Empty;
}
}

然后我们的sql就可以这样拼接了

left join MaintenanceTemplates it on it.Id = m.MaintenanceTemplateId
where m.IsDeleted = 0
{" and m.Code = @KeyWord ".If(!string.IsNullOrWhiteSpace(input.KeyWord))}
{" and m.ProjectId = @ProjectId ".If(input.ProjectId.HasValue)}
{" and a.ProductId = @ProductId ".If(input.ProductId.HasValue)}

比起以前又臭又长的if判断,个人感觉好多了。

Note:Dapper不会因为传多了参数而报错,所以放心使用If。

工作单元

使用EF的时候很方便做事务处理,而在Dapper中貌似就没那么优雅了。

我们每次在事务逻辑开始前都需要BeginTransaction开启,事务结束后都需要CommitTransaction提交。代码看起来也就稍显混乱。

如果我们通过特性标记的方式,在标记了UnitOfWork特性的方法自动开启和提交事务那就完美了。如下:

[UnitOfWork]
public virtual void Test()
{
//执行业务逻辑
}

当然,这是可行的。通过AOP拦截,在方法执行前开启事务,在方法执行后提交事务就可以了。

实现如下:

需要Nuget包Autofac.Extensions.DependencyInjection Autofac.Extras.DynamicProxy

[UnitOfWork]
public virtual void DelUser()
{
var sql = "select * from UserTemp";
var userList = dBContext.DbConnection.Query<object>(sql); var sql2 = $@"INSERT into UserTemp VALUES(0,'{DateTime.Now.ToString()}','sql2执行成功')";
dBContext.DbConnection.Execute(sql2);
throw new Exception("主动报错");//验证事务 是否有效 var sq3 = $@"INSERT into UserTemp VALUES(0,'{DateTime.Now.ToString()}','sq3执行成功')";
dBContext.DbConnection.Execute(sq3);
}
public class UnitOfWorkIInterceptor : IInterceptor
{
private DBContext dBContext;
public UnitOfWorkIInterceptor(DBContext dBContext)
{
this.dBContext = dBContext;
}
public void Intercept(IInvocation invocation)
{
MethodInfo methodInfo = invocation.MethodInvocationTarget;
if (methodInfo == null)
methodInfo = invocation.Method; UnitOfWorkAttribute transaction = methodInfo.GetCustomAttributes<UnitOfWorkAttribute>(true).FirstOrDefault();
//如果标记了 [UnitOfWork],并且不在事务嵌套中。
if (transaction != null && dBContext.Committed)
{
//开启事务
dBContext.BeginTransaction();
try
{
//事务包裹 查询语句
//https://github.com/mysql-net/MySqlConnector/issues/405
invocation.Proceed();
//提交事务
dBContext.CommitTransaction();
}
catch (Exception ex)
{
//回滚
dBContext.RollBackTransaction();
throw;
}
}
else
{
//如果没有标记[UnitOfWork],直接执行方法
invocation.Proceed();
}
}
}

完整的测试源码,会在文末提供。

SQL监控

使用EF的同学应该很多人都知道MiniProfiler,我在前些年分享EF的时候有做过简单介绍。

那么我们在执行Dapper的时候是不是也可以对生成的sql做检测和性能监控。

答案是肯定的。Git地址

MiniProfiler监控套件还真不是一般的强。EF、MongoDB、MySql、Redis、SqlServer统统支持。

接下来我们实现对Dapper监控,导入Nuget包MiniProfiler.AspNetCore

public class ActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var profiler = MiniProfiler.StartNew("StartNew");
using (profiler.Step("Level1"))
{
//执行Action
await next();
}
WriteLog(profiler);
} /// <summary>
/// sql跟踪
/// 下载:MiniProfiler.AspNetCore
/// </summary>
/// <param name="profiler"></param>
private void WriteLog(MiniProfiler profiler)
{
if (profiler?.Root != null)
{
var root = profiler.Root;
if (root.HasChildren)
{
root.Children.ForEach(chil =>
{
if (chil.CustomTimings?.Count > 0)
{
foreach (var customTiming in chil.CustomTimings)
{
var all_sql = new List<string>();
var err_sql = new List<string>();
var all_log = new List<string>();
int i = 1;
customTiming.Value?.ForEach(value =>
{
if (value.ExecuteType != "OpenAsync")
all_sql.Add(value.CommandString);
if (value.Errored)
err_sql.Add(value.CommandString);
var log = $@"【{customTiming.Key}{i++}】{value.CommandString} Execute time :{value.DurationMilliseconds} ms,Start offset :{value.StartMilliseconds} ms,Errored :{value.Errored}";
all_log.Add(log);
}); //TODO 日志记录
//if (err_sql.Any())
// Logger.Error(new Exception("sql异常"), "异常sql:\r\n" + string.Join("\r\n", err_sql), sql: string.Join("\r\n\r\n", err_sql));
//Logger.Debug(string.Join("\r\n", all_log), sql: string.Join("\r\n\r\n", all_sql));
}
}
});
}
}
}
}

运行效果:

Demo源码

完整的Demo源码:https://github.com/zhaopeiym/BlogDemoCode/tree/master/Dapper_Demo/DapperDemo

结束

最后给大家推荐一个开源项目quartzuihttps://github.com/zhaopeiym/quartzui

基于Quartz.NET 3.0的web管理界面,开箱即用。也可以完美运行在树莓派上。

docker run -v /fileData/quartzuifile:/app/File  --restart=unless-stopped --privileged=true --name quartzui -dp 5088:80 bennyzhao/quartzui:RaspberryPi

运行在普通PC或云主机上

docker run -v /fileData/quartzuifile:/app/File  --restart=unless-stopped --privileged=true --name quartzui -dp 5088:80 bennyzhao/quartzui

新建QQ群工控物联:995475200

Dapper的正确使用姿势的更多相关文章

  1. xpath轴的正确使用姿势

    网上看了许多关于轴的介绍,只介绍了语法,而没有明说具体实际中该怎么使用,百思不得其解. 背景--python中使用xpath:  ----------------------------------- ...

  2. 高版本jquery尤其是1.10.2的版本设置input radio设置值的最正确的姿势。

    $("input:radio[name="analyshowtype"]").attr("checked",false); $(" ...

  3. NSnotificationCenter 正确使用姿势, removeObject 探索

    最近在做平板的过程中,发现了一些很不规范的代码.偶然修复支付bug的时候,看到其他项目代码,使用通知的地方没有移除,我以为我这个模块的支付闪退是因为他通知没有移除的缘故.而在debug和看了具体的代码 ...

  4. 微信H5中静默登录及非静默登录的正确使用姿势

    在微信中打开网页且需要调用微信登录接口时,微信官方给我们提供了两种登录调用方式:静默登录和非静默登录:但是官方文档中却没有说明在何种情况下使用静默登录,何种情况下使用非静默登录,所以在这里,我想将之前 ...

  5. Java日志正确使用姿势

    前言 关于日志,在大家的印象中都是比较简单的,只须引入了相关依赖包,剩下的事情就是在项目中“尽情”的打印我们需要的信息了.但是往往越简单的东西越容易让我们忽视,从而导致一些不该有的bug发生,作为一名 ...

  6. MongoDB系列:五、MongoDB Driver使用正确的姿势连接复制集

    MongoDB复制集(Replica Set)通过存储多份数据副本来保证数据的高可靠,通过自动的主备切换机制来保证服务的高可用.但需要注意的时,连接副本集的姿势如果不对,服务高可用将不复存在. 使用复 ...

  7. 基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势)

    基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势)   前言 前几天对Apollo配置中心的demo进行一个部署试用,现公司已决定使用,这两天进行分布式部署的时候 ...

  8. 玩转java多线程(wait和notifyAll的正确使用姿势)

    转载请标明博客的地址 本人博客和github账号,如果对你有帮助请在本人github项目AioSocket上点个star,激励作者对社区贡献 个人博客:https://www.cnblogs.com/ ...

  9. Gradle的依赖方式——Lombok在Gradle中的正确配置姿势

    写过java的都知道,lombok几乎在项目中处于不可或缺的一部分,但是lombok在Gradle的项目中配置并非人人都知道. 很多人在项目依赖中直接这样写 1 compile "org.p ...

随机推荐

  1. OpenCV4Android编译

    http://blog.sina.com.cn/s/blog_602f87700102vdnw.html (2015-04-02 11:10:01) 转载▼     最近的一个项目中,需要自己编译Op ...

  2. (转)Understanding C parsers generated by GNU Bison

    原文链接:https://www.cs.uic.edu/~spopuri/cparser.html Satya Kiran PopuriGraduate StudentUniversity of Il ...

  3. Linux 中权限控制实例

    前言 前文对 Linux 中的权限进行了较为透彻的分析.而本文,则在前文的基础上,具体说明如何在代码中进行权限控制. 下面的代码涉及到以下几个方面: 1. 创建文件时设置文件权限 2. 修改文件的默认 ...

  4. [数据挖掘课程笔记]Naïve Bayesian Classifier

    朴素贝叶斯模型 1) X:一条未被标记的数据 2) H:一个假设,如H=X属于Ci类 根据贝叶斯公式 把X表示为(x1,x2,....xn) x1,x2,....xn表示X在各个特征上的值. 假设有c ...

  5. Appium——详解Appium server capabilities

      appium server capabilities来告诉appium,如何运行自动化测试,因此需要详细了解. 官方文档:http://appium.io/slate/en/master/?rub ...

  6. php排序方法之选择排序

    //选择排序法 $arr = array(3,55,45,2,67,76,6.7,-65,85,4); function selectSort($arr){ for ( $i=0; $i<cou ...

  7. 51nod1674:区间的价值2(分治,利用&和|的收敛性)

    lyk拥有一个区间. 它规定一个区间的价值为这个区间中所有数and起来的值与这个区间所有数or起来的值的乘积. 例如3个数2,3,6.它们and起来的值为2,or起来的值为7,这个区间对答案的贡献为2 ...

  8. 使用 WinSCP(下载) 上文件到 Linux图文教程

        问题导读: 1.如何远程链接? 2.如何上传文件? 3.如何对立面的文件进行操作? 4.什么情况下会链接失败? https://yunpan.cn/cYWtNMycjeVPv 访问密码 4f7 ...

  9. BZOJ_1004_[HNOI2008]Cards_burnside+DP

    BZOJ_1004_[HNOI2008]Cards_burnside+DP Description 小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问 ...

  10. 初学Java(一)

    基本语法: 编写Java程序时,应注意以下几点: 1.大小写敏感:java是大小写敏感的,这就意味着标识符Hello与hello是不同的. 2.类名:对于所有的类来说,类名的首字母应该大写.如果类名由 ...