数据访问层之Repository
本章我们继续IRepository开发,这个仓储与领域模式里边的仓储有区别,更像一个工具类,也就是有些园友说的“伪仓储”,
这个仓储只实现单表的CURD与Query,都是通过主键ID或拉姆达表达式进行操作的,返回的都是单表的实体或实体集合,
多表的在IQuery接口中再讲;虽然如此,但是如果与“活动记录”开发模式搭配的话,会非常合适,可以减少开发的时间
及出错几率,更符合开发人员的类型调用习惯
IRepository.cs
1 public interface IRepository<T> where T : class 2 { 3 void Add(T entity); 4 void AddBatch(IEnumerable<T> entitys); 5 void Update(T entity); 6 void Delete(T entity); 7 void Delete(string Id); 8 void Delete(int Id); 9 void Delete(Guid Id); 10 T Get(string Id); 11 T Get(Guid Id); 12 T Get(int Id); 13 T Get(T entity); 14 T Get(Expression<Func<T, bool>> func); 15 IEnumerable<T> GetAll(); 16 IEnumerable<T> GetList(Expression<Func<T, bool>> where = null, Expression<Func<T, bool>> order = null); 17 Tuple<int, IEnumerable<T>> GetPage(Page page, Expression<Func<T, bool>> where = null, Expression<Func<T, bool>> order = null); 18 long Count(Expression<Func<T, bool>> where = null); 19 }
仓储的实现
这里我们只实现dapper的适配,EF有时间再搞吧
dapper大家应该都比较熟悉吧,不懂的朋友可以在园中搜索一下啊,很多案例
DapperRepository.cs
1 using Dapper.Contrib.Extensions; 2 using LjrFramework.Common; 3 using LjrFramework.Interface; 4 using System; 5 using System.Collections.Generic; 6 using System.Data; 7 using System.Linq; 8 using System.Linq.Expressions; 9 10 namespace LjrFramework.Data.Dapper 11 { 12 public class DapperRepository<T> : IRepository<T> where T : class 13 { 14 protected IDbConnection Conn { get; private set; } 15 16 public DapperRepository() 17 { 18 Conn = DbConnectionFactory.CreateDbConnection(); 19 } 20 21 public void SetDbConnection(IDbConnection conn) 22 { 23 Conn = conn; 24 } 25 26 public void Add(T entity) 27 { 28 Conn.Insert<T>(entity); 29 } 30 31 public void AddBatch(IEnumerable<T> entitys) 32 { 33 foreach (T entity in entitys) 34 { 35 Add(entity); 36 } 37 } 38 39 public void Update(T entity) 40 { 41 Conn.Update(entity); 42 } 43 44 public void Delete(T entity) 45 { 46 Conn.Delete(entity); 47 } 48 49 public void Delete(string Id) 50 { 51 var entity = Get(Id); 52 if (entity == null) return; 53 54 Delete(entity); 55 } 56 57 public void Delete(int Id) 58 { 59 var entity = Get(Id); 60 if (entity == null) return; 61 62 Delete(entity); 63 } 64 public void Delete(Guid Id) 65 { 66 var entity = Get(Id); 67 if (entity == null) return; 68 69 Delete(entity); 70 } 71 72 public T Get(T entity) 73 { 74 return Conn.Get<T>(entity); 75 } 76 77 public T Get(Guid Id) 78 { 79 return Conn.Get<T>(Id); 80 } 81 82 public T Get(string Id) 83 { 84 return Conn.Get<T>(Id); 85 } 86 87 public T Get(int Id) 88 { 89 return Conn.Get<T>(Id); 90 } 91 92 public T Get(Expression<Func<T, bool>> func) 93 { 94 var linqToWhere = new LinqToWhere<T>(); 95 linqToWhere.Parse(func); 96 97 return Conn.GetByFunc<T>(linqToWhere.Where, linqToWhere.KeyValuePairs); 98 } 99 100 public IEnumerable<T> GetAll() 101 { 102 return Conn.GetAll<T>(); 103 } 104 105 public IEnumerable<T> GetList(Expression<Func<T, bool>> where = null, Expression<Func<T, bool>> order = null) 106 { 107 where = where.And(order); 108 109 var linqToWhere = new LinqToWhere<T>(); 110 linqToWhere.Parse(where); 111 112 return Conn.GetListByFunc<T>(linqToWhere.Where, linqToWhere.KeyValuePairs); 113 } 114 115 public Tuple<int, IEnumerable<T>> GetPage(Page page, Expression<Func<T, bool>> where = null, Expression<Func<T, bool>> order = null) 116 { 117 where = where.And(order); 118 119 var linqToWhere = new LinqToWhere<T>(); 120 linqToWhere.Parse(where); 121 122 var multi = Conn.GetPage<T>(page.PageIndex, page.PageSize, linqToWhere.Order, linqToWhere.Where, linqToWhere.KeyValuePairs); 123 var count = multi.Read<int>().Single(); 124 var results = multi.Read<T>(); 125 126 return new Tuple<int, IEnumerable<T>>(count, results); 127 } 128 129 public long Count(Expression<Func<T, bool>> where = null) 130 { 131 var linqToWhere = new LinqToWhere<T>(); 132 linqToWhere.Parse(where); 133 134 return Conn.Count<T>(linqToWhere.Where, linqToWhere.KeyValuePairs); 135 } 136 } 137 }
注意标红的那行,Conn的所有方法都是在命名空间(Dapper.Contrib.Extensions)下的扩展方法
我们看看其中的Insert实现方式,为什么直接传递T就可以,而不用写sql语句
可以看到,dapper后台是遍历实体的属性,最后也是拼凑成符合格式的sql语句;
这一点也可以自己扩展,有很大的便利性,所以他写在Extensions中
DbConnectionFactory.cs 也很简单,是dapper支持多数据库的工厂类
1 public class DbConnectionFactory 2 { 3 private static readonly string connectionString; 4 private static readonly string databaseType; 5 6 static DbConnectionFactory() 7 { 8 var collection = ConfigurationManager.ConnectionStrings["connectionString"]; 9 connectionString = collection.ConnectionString; 10 databaseType = collection.ProviderName.ToLower(); 11 } 12 13 public static IDbConnection CreateDbConnection() 14 { 15 IDbConnection connection = null; 16 switch (databaseType) 17 { 18 case "system.data.sqlclient": 19 connection = new System.Data.SqlClient.SqlConnection(connectionString); 20 break; 21 case "mysql": 22 //connection = new MySql.Data.MySqlClient.MySqlConnection(connectionString); 23 break; 24 case "oracle": 25 //connection = new Oracle.DataAccess.Client.OracleConnection(connectionString); 26 //connection = new System.Data.OracleClient.OracleConnection(connectionString); 27 break; 28 case "db2": 29 connection = new System.Data.OleDb.OleDbConnection(connectionString); 30 break; 31 default: 32 connection = new System.Data.SqlClient.SqlConnection(connectionString); 33 break; 34 } 35 return connection; 36 } 37 }
自此,dapper适配的仓储就完成了
我们在测试项目中看看效果,这里我们不在继续在基础设施里添加仓储了,用另一种方式:IOC
项目引用Autofac,用依赖出入来初始化IRepository<T>接口
测试仓储功能
1 [TestClass] 2 public class DapperRepositoryTest 3 { 4 private IRepository<LoginUser> repository; 5 6 public DapperRepositoryTest() 7 { 8 var builder = new ContainerBuilder(); 9 builder.RegisterType<DapperRepository<LoginUser>>().As<IRepository<LoginUser>>(); 10 11 var container = builder.Build(); 12 repository = container.Resolve<IRepository<LoginUser>>(); 13 } 14 15 [TestMethod] 16 public void Add() 17 { 18 var loginUser = new LoginUser() 19 { 20 Id = Guid.NewGuid(), 21 LoginName = "lanxiaoke-" + Guid.NewGuid().ToString(), 22 Password = "mima1987", 23 IsEnabled = 1, 24 CreateTime = DateTime.Now 25 }; 26 27 repository.Add(loginUser); 28 29 long count = repository.Count(t => t.LoginName == loginUser.LoginName); 30 31 Assert.AreEqual(true, count == 1); 32 } 33 34 [TestMethod] 35 public void Get() 36 { 37 var loginUser = new LoginUser() 38 { 39 Id = Guid.NewGuid(), 40 LoginName = "lanxiaoke-" + Guid.NewGuid().ToString(), 41 Password = "mima1987", 42 IsEnabled = 1, 43 CreateTime = DateTime.Now 44 }; 45 repository.Add(loginUser); 46 47 var tmp = repository.Get(loginUser.Id); 48 Assert.AreEqual(loginUser.Id, tmp.Id); 49 50 var tmp2 = repository.Get(w => w.Id == loginUser.Id && w.IsEnabled == loginUser.IsEnabled); 51 Assert.AreEqual(loginUser.Id, tmp2.Id); 52 } 53 ...//限于篇幅,只写这么多了,大部分代码都差不多 54 }
注意这句:container.Resolve<IRepository<LoginUser>>(); 这句就是实现初始化IRepository<T>接口;
如何初始化呢?看上一句:builder.RegisterType<DapperRepository<LoginUser>>().As<IRepository<LoginUser>>(); 直接注册DapperRepository就可以了
其实这里也可以用配置的方式初始化IRepository<T>,这样就可以避免DapperRepository<T>与业务层耦合了
测试项目,我们就暂且这么写吧。
我们来看看效果
下边都是这次测试生成的数据
自此 IRepository 就开发完成了