项目架构开发:数据访问层之Query
上一章我们讲了IRepository接口,这张我们来讲IQuery
根据字面意思就可以知道,这次主要讲数据查询,上一章我们只针对单表做了查询的操作,多表联查并没有实现
其实对于任何一个项目来说,多表联查都是比较麻烦的地方,因为项目的“读”操作,特别是多表的“读”,至少占据所有“读”的一半以上
然而至今,据我所知还没有哪一款ORM工具可以灵活处理多表联查;想要不写sql语句,又想性能高,还想用强类型的ling查询方法;这对于多表查询来说比较难
鉴于此,也别做那些别扭的映射了(像NH),而如果用lingtosql我觉得还不如直接写sql来的好;
是开发人员不可能不懂sql,那对多表查询这一块,干脆独立一个Query类出来,专门处理这种事
好了这只是我的处理方式,我们来看看
IQuery.cs
1 public interface IQuery 2 { 3 T QuerySingle<T>(string sql, object paramPairs) where T : class; 4 IEnumerable<T> QueryList<T>(string sql, object paramPairs) where T : class; 5 6 /// <summary>必须带上row_number() over({0}) RowNumber</summary> 7 Tuple<int, IEnumerable<T>> GetPage<T>(Page page, string sql, dynamic paramPairs = null) where T : class; 8 9 Tuple<int, IEnumerable<T>> GetPage<T>(string sql, object paramPairs = null) where T : class; 10 int Execute(string sql, dynamic paramPairs = null); 11 long Count(string sql, dynamic paramPairs = null); 12 }
上方法名就可以知道,写sql,然后返回自定义的T或IEnumerable<T>
这里避免了用泛型类,因为某个实体大多数情况下可能也只是需要,上边其中其中的1、2个而已
这里有一个Execute方法,其实不应该放在这的,但是也懒得写另外一个类了,先放着吧
Query的实现
DapperQuery.cs
1 using Dapper; 2 using Dapper.Contrib.Extensions; 3 using LjrFramework.Common; 4 using LjrFramework.Interface; 5 using System; 6 using System.Collections.Generic; 7 using System.Data; 8 using System.Linq; 9 10 namespace LjrFramework.Data.Dapper 11 { 12 public class DapperQuery : IQuery 13 { 14 protected IDbConnection Conn { get; private set; } 15 16 public DapperQuery() 17 { 18 Conn = DbConnectionFactory.CreateDbConnection(); 19 } 20 21 public void SetDbConnection(IDbConnection conn) 22 { 23 Conn = conn; 24 } 25 26 public T QuerySingle<T>(string sql, object paramPairs) where T : class 27 { 28 return Conn.Query<T>(sql, paramPairs).SingleOrDefault(); 29 } 30 31 public IEnumerable<T> QueryList<T>(string sql, object paramPairs) where T : class 32 { 33 return Conn.Query<T>(sql, paramPairs); 34 } 35 36 /// <summary>自动分页,必须带上row_number() over({0}) RowNumber</summary> 37 public Tuple<int, IEnumerable<T>> GetPage<T>(Page page, string sql, object paramPairs = null) where T : class 38 { 39 var multi = Conn.GetPage<T>(page.PageIndex, page.PageSize, sql, paramPairs); 40 var count = multi.Read<int>().Single(); 41 var results = multi.Read<T>(); 42 return new Tuple<int, IEnumerable<T>>(count, results); 43 } 44 // 需自己实现分页语句 45 public Tuple<int, IEnumerable<T>> GetPage<T>(string sql, object paramPairs = null) where T : class 46 { 47 var multi = Conn.GetGridReader<T>(sql, paramPairs); 48 var count = multi.Read<int>().Single(); 49 var results = multi.Read<T>(); 50 return new Tuple<int, IEnumerable<T>>(count, results); 51 } 52 53 54 public int Execute(string sql, object paramPairs = null) 55 { 56 return Conn.Execute(sql, paramPairs); 57 } 58 59 public long Count(string sql, object paramPairs = null) 60 { 61 return Conn.Query<long>(sql, paramPairs).SingleOrDefault(); 62 } 63 } 64 }
这个实现跟上一章差不多,都是直接调用Conn的扩展方法,
我们进去看看(public Tuple<int, IEnumerable<T>> GetPage<T>(string sql, object paramPairs = null) where T : class)的实现
可以看到dapper是已经支持sql语句的查询,并且返回多个记录(SqlMapper.GridReader)
每次分页都要去手动刷选 页数 与 记录开始行数的话比较麻烦
所有我做了一点小改动,武学用户写分页语句,直接查询就可以了,后台会自动生成分页语句的格式
不过要带上row_number() over({0}) RowNumber;就是上边那个有注释的方法
他的实现如下
这样用户只需要准守一点约束,就会方便很多
这些都是在dapper的Extensions里实现的,用户需要自行修改,自己想要的自定义的功能
好了,Query有效代码写完了,我们看看运行效果
Query测试
1 using Autofac; 2 using Company.Project.PO; 3 using LjrFramework.Data.Dapper; 4 using LjrFramework.Common; 5 using LjrFramework.Infrastructure; 6 using LjrFramework.Interface; 7 using Microsoft.VisualStudio.TestTools.UnitTesting; 8 using System; 9 using LjrFramework.Data.UnitOfWork; 10 11 namespace LjrFramework.UnitTest 12 { 13 [TestClass] 14 public class QueryTest 15 { 16 private IQuery query; 17 18 public QueryTest() 19 { 20 var builder = new ContainerBuilder(); 21 builder.RegisterType<DapperQuery>().As<IQuery>(); 22 23 var container = builder.Build(); 24 query = container.Resolve<IQuery>(); 25 26 } 27 28 [TestMethod] 29 public void QuerySingle() 30 { 31 var model = query.QuerySingle<LoginUser>("select * from LoginUser where Id = @Id", new { Id = "854B1FCA-F8D7-4B4B-AA5D-9075F1922721" }); 32 33 Assert.AreEqual(model.LoginName, "lanxiaoke-d318fd40-1b9d-42f8-a002-388b1228012d"); 34 } 35 36 [TestMethod] 37 public void QueryList() 38 { 39 var list = query.QueryList<LoginUser>("select * from LoginUser where LoginName like '%'+ @LoginName + '%'", new { LoginName = "lanxiaoke" }); 40 41 int index = 0; 42 foreach (var user in list) 43 { 44 index++; 45 } 46 47 Assert.AreEqual(index > 0, true); 48 } 49 50 [TestMethod] 51 public void GetPage() 52 { 53 var page = new Page() 54 { 55 PageIndex = 1, 56 PageSize = 10 57 }; 58 59 var results = query.GetPage<LoginUser>(page, 60 @"select row_number() over(order by CreateTime) RowNumber,* from LoginUser where LoginName like '%'+ @LoginName + '%'", new { LoginName = "lanxiaoke" }); 61 62 var total = results.Item1; 63 var list = results.Item2; 64 65 int index = 0; 66 foreach (var user in list) 67 { 68 index++; 69 } 70 71 Assert.AreEqual(index > 0, true); 72 } 73 74 [TestMethod] 75 public void GetPage2() 76 { 77 var page = new Page() 78 { 79 PageIndex = 1, 80 PageSize = 10 81 }; 82 83 var results = query.GetPage<LoginUser>( 84 @"select count(*) as TotalCount from LoginUser c where LoginName like '%'+ @LoginName + '%' 85 select row_number() over(order by CreateTime) RowNumber,* from LoginUser where LoginName like '%'+ @LoginName + '%'", 86 new { LoginName = "lanxiaoke" }); 87 88 var total = results.Item1; 89 var list = results.Item2; 90 91 int index = 0; 92 foreach (var user in list) 93 { 94 index++; 95 } 96 97 Assert.AreEqual(index > 0, true); 98 } 99 100 [TestMethod] 101 public void Count() 102 { 103 var row = query.Count("select count(*) from LoginUser"); 104 105 Assert.AreEqual(row > 1, true); 106 } 107 108 } 109 }
自此,多表查询就讲完了
项目架构开发系列
- 项目架构开发:数据访问层之Cache
- 项目架构开发:数据访问层之Logger
- 项目架构开发:数据访问层之Repository
- 项目架构开发:数据访问层之Query
- 项目架构开发:数据访问层之UnitOfWork