Dapper是什么?
Dapper是什么?
Dapper是开源的、轻量级,高性能的ORM(对象关系映射)。
Dapper的特点:
一、轻量:Dapper的GitHub地址:https://github.com/StackExchange/Dapper/tree/main/Dapper,它的核心代码是SqlMapper.cs。代码量不多,编辑后所占空间也比较小。
二、高性能:它通过Emit反射IDataReader的序列队列,快速的映射出对象。
三、Dapper更倾向于面向SQL,支持多种数据库。
.NetCore中简单封装使用Dapper
1、引入Dapper和Dapper.Contrib
2、创建仓储接口
1 public interface IRepository<T> where T:BaseEntity 2 { 3 T GetById(string id); 4 IEnumerable<T> GetAll(); 5 IEnumerable<T> GetBySql(string sql, Dictionary<string, object> parameter, CommandType commandType); 6 IEnumerable<T> GetByPaging(string sql, Dictionary<string, object> parameters, CommandType commandType); 7 8 void Add(T entity); 9 void Delete(T entity); 10 void Update(T Entity); 11 12 }
3、创建仓储基类
1 public class BaseRepository<T> : IRepository<T> where T : BaseEntity 2 { 3 private IDbConnection _connection; 4 public BaseRepository(IConfiguration configuration) 5 { 6 this._connection = new MySqlConnection(configuration.GetConnectionString("ListingDb")); 7 } 8 9 public IEnumerable<T> GetAll() 10 { 11 var result = _connection.GetAll<T>(); 12 return result; 13 } 14 15 public T GetById(string id) 16 { 17 var result = _connection.Get<T>(id); 18 return result; 19 } 20 21 public IEnumerable<T> GetByPaging(string sql, Dictionary<string, object> parameters, CommandType commandType) 22 { 23 //TODO query paging 24 return null; 25 } 26 27 public IEnumerable<T> GetBySql(string sql, Dictionary<string, object> parameters, CommandType commandType) 28 { 29 var result = _connection.Query<T>(sql, parameters, commandType: commandType); 30 return result; 31 } 32 33 public void Add(T entity) 34 { 35 _connection.Insert(entity, null); 36 } 37 public void Update(T entity) 38 { 39 _connection.Update(entity); 40 } 41 public void Delete(T entity) 42 { 43 _connection.Delete(entity); 44 } 45 }
4、在startup.cs中注入仓储类
services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
5、定义具体业务类进行调用(略)
进阶--实现Dapper+UnitOfWork
想了解UOW(UnitOfWork)可参照:https://martinfowler.com/eaaCatalog/unitOfWork.html
在【简单封装dapper】中,不难发现它主要是针对单个对象的操作。即使通过注入多个对象的仓储进行统一处理统一事务的多表,但是还是缺少了事务的支持。就像之前写的关于数据库访问的封装(https://www.cnblogs.com/johnyong/p/14152891.html)一样,需要将事务进行抽离,即做成单独的工作单元,实现对对象状态的跟踪,实现事务。
以下是简单的实现
1、定义IUnitOfWork接口
1 public interface IUnitOfWork 2 { 3 Guid Id { get; } 4 IDbConnection Connection { get; } 5 IDbTransaction Transaction { get; } 6 void Begin(); 7 void Commit(); 8 void Rollback(); 9 }
2、定义UnitOfWork
1 public sealed class UnitOfWork : IUnitOfWork, IDisposable 2 { 3 private Guid _id = Guid.Empty; 4 private IDbConnection _connection = null; 5 private IDbTransaction _transaction = null; 6 private bool _disposed = false; 7 8 public UnitOfWork(IConfiguration configuration) 9 { 10 _id = new Guid(); 11 _connection = new MySqlConnection(configuration.GetConnectionString("SimpleTestConnection")); 12 13 14 } 15 16 17 public Guid Id 18 { 19 get { return _id; } 20 } 21 22 public IDbConnection Connection 23 { 24 get { return _connection; } 25 } 26 public IDbTransaction Transaction 27 { 28 get { return _transaction; } 29 } 30 31 public void Begin() 32 { 33 if (_connection.State != ConnectionState.Open) 34 { 35 _connection.Open(); 36 } 37 _transaction = _connection.BeginTransaction(); 38 } 39 40 public void Commit() 41 { 42 _transaction.Commit(); 43 Dispose(); 44 } 45 46 public void Rollback() 47 { 48 _transaction.Rollback(); 49 Dispose(); 50 } 51 52 53 public void Dispose() 54 { 55 Dispose(true); 56 GC.SuppressFinalize(this); 57 } 58 59 public void Dispose(bool disposing) 60 { 61 if (_disposed) 62 return; 63 64 if (disposing) 65 { 66 _transaction?.Dispose(); 67 _connection?.Dispose(); 68 } 69 70 _transaction = null; 71 _connection = null; 72 _disposed = true; 73 } 74 ~UnitOfWork() 75 { 76 Dispose(false); 77 } 78 }
3、修改BaseRepository
1 public class BaseRepository<T> : IRepository<T> where T : BaseEntity 2 { 3 private IUnitOfWork _unitOfWork; 4 public BaseRepository(IUnitOfWork unitOfWork) 5 { 6 this._unitOfWork = unitOfWork; 7 } 8 9 public IEnumerable<T> GetAll() 10 { 11 var result = _unitOfWork.Connection.GetAll<T>(); 12 return result; 13 } 14 15 public T GetById(string id) 16 { 17 var result = _unitOfWork.Connection.Get<T>(id); 18 return result; 19 } 20 21 public IEnumerable<T> GetByPaging(string sql, Dictionary<string, object> parameters, CommandType commandType) 22 { 23 //TODO query paging 24 return null; 25 } 26 27 public IEnumerable<T> GetBySql(string sql, Dictionary<string, object> parameters, CommandType commandType) 28 { 29 var result = _unitOfWork.Connection.Query<T>(sql, parameters, commandType: commandType); 30 return result; 31 } 32 33 public void Add(T entity) 34 { 35 _unitOfWork.Connection.Insert(entity,null); 36 } 37 public void Update(T entity) 38 { 39 _unitOfWork.Connection.Update(entity); 40 } 41 public void Delete(T entity) 42 { 43 _unitOfWork.Connection.Delete(entity); 44 } 45 }
4、定义事务特性,用于实现aop(需要引入AspectCore.DynamicProxy)
1 public class SystemTransactionAttribute : AbstractInterceptorAttribute 2 { 3 IUnitOfWork _unitOfWork { get; set; } 4 5 public async override Task Invoke(AspectContext context, AspectDelegate next) 6 { 7 try 8 { 9 _unitOfWork = context.ServiceProvider.GetService(typeof(IUnitOfWork)) as IUnitOfWork; 10 _unitOfWork.Begin(); 11 await next(context); 12 _unitOfWork.Commit(); 13 } 14 catch (Exception ex ) 15 { 16 _unitOfWork.Rollback(); 17 throw new Exception("SystemTransaction error",ex); 18 } 19 } 20 }
5、注入服务
在startup.cs注入UnitOfWork
services.AddScoped<IUnitOfWork, UnitOfWork>(); services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
修改Program.cs
1 public class Program 2 { 3 public static void Main(string[] args) 4 { 5 CreateHostBuilder(args).Build().Run(); 6 } 7 8 public static IHostBuilder CreateHostBuilder(string[] args) => 9 Host.CreateDefaultBuilder(args) 10 .ConfigureWebHostDefaults(webBuilder => 11 { 12 webBuilder.UseStartup<Startup>(); 13 }).UseDynamicProxy(); 14 }
6、测试示例代码
1 public class ListingService: IListingService 2 { 3 private IRepository<Listing> _listingRepository; 4 private IRepository<ListingDetail> _listingDetailRepository; 5 6 public ListingService(IRepository<Listing> listingRepository,IRepository<ListingDetail> listingDetailRepository) 7 { 8 this._listingRepository = listingRepository; 9 this._listingDetailRepository = listingDetailRepository; 10 } 11 [SystemTransaction] 12 public void AddListing(ListingContract contract) 13 { 14 var listing = new Listing() 15 { 16 Id = ObjectId.GenerateNewId().ToString(), 17 Price = contract.Price, 18 Quantity = contract.Quantity, 19 Title=contract.Title, 20 Remark="john_yong test" 21 }; 22 _listingRepository.Add(listing); 23 foreach (var item in contract.DetailList) 24 { 25 var listingDetail = new ListingDetail() 26 { 27 Id ="",//设置Id为空抛出异常 //ObjectId.GenerateNewId().ToString(), 28 ListingId = listing.Id, 29 Quantity = item.Quantity, 30 SKU = item.SKU, 31 }; 32 _listingDetailRepository.Add(listingDetail); 33 } 34 35 } 36 }
注:
使用dapper.Contrib,默认数据库表明为实体名称的复数形式,我们可以使用[Table]特性进行声明表名称。
Dapper.Contrib.Extensions扩展而言:自增主键用[Key]标志(默认为自增主键),非自增主键必须用[ExplicitKey]标志,否则即使我们给Id指定值再进行插入数据表,最终Id还是为空。