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】中,不难发现它主要是针对单个对象的操作。即使通过注入多个对象的仓储进行统一处理统一事务的多表,但是还是缺少了事务的支持。就像之前写的关于数据库访问的封装()一样,需要将事务进行抽离,即做成单独的工作单元,实现对对象状态的跟踪,实现事务。
以下是简单的实现
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还是为空。