本文demo适用于MySQL
高性能、易排查、易运维、灵活可控html
和EF相比,手写sql当修改表结构不易发现bug。
习惯了EF后再来使用Dapper,会很难适应那种没有了强类型的安全感。不过能够用单元测和心细来避免。mysql
问题:IDbConnection需不须要手动Open打开链接
答案:有时候须要有时候不须要git
Dapper链接可分两种:主动管理(本身管理链接的打开和关闭)和自动管理(自动管理链接的打开和关闭)github
//短短三行代码即实现了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#L530web
Note:ADO.NET默认是启用链接池的 Pooling = true,链接池中最大链接数,默认为100
sql
在使用Dapper的过程当中,你有可能遇到过链接池超过最大限制。那问题是怎么来的呢?
若是主动管理或者自动管理链接都不会有问题。就怕你管理一半,打开不关闭:docker
//循环执行两百次左右就能够重现链接池超过最大限制 DBContext dBContext2 = new DBContext(); dBContext2.DbConnection.Open();
解决办法相信不用我说了。数据库
Note:在使用事务的时候须要手动打开链接,请不要忘记在finally里面Close。
//一、可经过匿名对象集合进行参数化数据新增。(性能优化参考3) DbConnection.Execute(sqlStr, ListEntity); //二、【sql拼接可大大优化执行效率】在values后面带上多有要插入的值。(若是数据太大可分批插入,如1000条一提交) insert into tt (a,b,c,d) values (50,1,'1','1'), (51,2,'1','2'); //三、参数化防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)
//一、可经过匿名对象集合进行参数化数据修改。(须要修改的值都不同的状况下,性能优化参考4) DbConnection.Execute(sqlStr, ListEntity); //二、若是须要修改的值都是同样,只是条件不同。(使用SQL语句中的IN语法) DbConnection.Execute("UPDATE tt SET aa = @aa where bb in @bb;", new { aa, bb }); //三、快速批量修改(此方法很是适合`新增或修改`数据的场景,可经过建联合惟一索引来实现新增或修改的区分。【组合字段不能为空,不然为空 不作惟一,有重复空数据】) insert into test_tbl (id,dr) values (1,'2'),(2,'3'),...(x,'y') on duplicate key update dr=values(dr); //四、参数化防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();//错误
使用过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(); } } }
完整的测试源码,会在文末提供。
使用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源码:https://github.com/zhaopeiym/BlogDemoCode/tree/master/Dapper_Demo/DapperDemo
最后给你们推荐一个开源项目quartzui:https://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