同時支持EF+Dapper的混合倉儲,助你快速搭建數據訪問層

  • 2019 年 10 月 8 日
  • 筆記

背景

17年開始,公司開始向DotNet Core轉型,面對ORM工具的選型,當時圍繞Dapper和EF發生了激烈的討論。項目團隊更加關注快速交付,他們主張使用EF這種能快速開發的ORM工具;而在線業務團隊對性能有更高的要求,他們更希望使用能直接執行Sql語句的Dapper,這樣可控性更高。而對於架構團隊來說,滿足開發團隊的各種需求,提高他們的開發效率是最核心的價值所在,所以當時決定做一個混合型的既支持EF又支持dapper的數據倉儲。

為什麼選擇EF+Dapper

目前來說EF和Dapper是.NET平台最主流的ORM工具,團隊成員的接受程度很高,相關的資料非常齊全,學習成本很低,各種坑也最少。

介紹

  1. 它不是一個ORM工具,它不做任何關於數據底層的操作
  2. 它是一個簡易封裝的數據庫倉儲和工作單元模型
  3. 能幫助你快速的構建項目的數據訪問層
  4. 經過了2年多時間,10個項目組,大小近100多個線上項目的考驗
  5. 支持EF和Dapper,可以在項目中隨意切換使用
  6. 支持工作單元模式,也支持傳統事務
  7. 支持Mysql和Mssql
  8. 支持同步和異步操作,推薦使用異步

PS: 簡單操作使用EF,複雜sql操作使用Dapper是快速開發的秘訣。

使用方法

引入nuget

<PackageReference Include="Leo.Chimp" Version="2.1.1" />

創建實體對象,繼承IEntity

public class School : IEntity  {      public Guid Id { get; set; }      public string Name { get; set; }  }

創建倉儲接口和實現類,分別繼承IRepository和EfCoreRepository

public interface ISchoolRepository : IRepository<School>  {  }  public class SchoolRepository: EfCoreRepository<School>,ISchoolRepository  {      public SchoolRepository(DbContext context) : base(context)      {      }  }

創建上下文,繼承BaseDbContext,如果你不需要操作上下文可以不用做這一步

public class ChimpDbContext : BaseDbContext  {      public ChimpDbContext(DbContextOptions options) : base(options)      {      }      protected override void OnModelCreating(ModelBuilder modelBuilder)      {          base.OnModelCreating(modelBuilder);          //your code      }  }

注入服務

services.AddChimp<ChimpDbContext>(                  opt =>                  opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123")  );

如果你沒有創建上下文

services.AddChimp(                  opt =>                  opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123")  );

在Controller中使用

public class ValuesController : ControllerBase  {      private readonly ISchoolRepository _schoolRepository;      private readonly IUnitOfWork _unitOfWork;      public ValuesController(ISchoolRepository schoolRepository, IUnitOfWork unitOfWork)      {          _schoolRepository = schoolRepository;          _unitOfWork = unitOfWork;      }  }

詳細使用說明

查詢

//根據主鍵查詢  _schoolRepository.GetById(Id)
//不帶追蹤的查詢,返回數據不能用於更新或刪除操作,性能快  schoolRepository.TableNoTracking.First(x => x.Id == Id);
//帶追蹤的查詢,返回數據可以用於更新或刪除操作,性能稍慢  schoolRepository.Table.First(x => x.Id == Id);
//分頁查詢  _schoolRepository.TableNoTracking.ToPagedList(1,10);
//sql語句查詢  _unitOfWork.QueryAsync<School>("select * from school");  
//sql分頁查詢  _unitOfWork.QueryPagedListAsync<School>(1, 10, "select * from school order by id");

關於查詢,暴露了返回IQueryable的TableNoTracking、Table這兩個屬性,讓開發人員自己組裝Lambda表達式進行查詢操作

新增

//新增,支持批量新增  _schoolRepository.Insert(school);  await _unitOfWork.SaveChangesAsync();
//sql語句新增  await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",school);  await _unitOfWork.SaveChangesAsync();

編輯

//編輯,支持批量編輯  var school = await _schoolRepository.GetByIdAsync(Id);  school.Name="newschool";  _schoolRepository.Update(school);  await _unitOfWork.SaveChangesAsync();
//編輯,不用先查詢  var school = new School  {      Id = "xxxxxx",      Name = "newschool"  };  _schoolRepository.Update(school, x => x.Name);  await _unitOfWork.SaveChangesAsync();
//sql語句編輯  await _unitOfWork.ExecuteAsync("update school set name=@Name where id=@Id",school);  await _unitOfWork.SaveChangesAsync();

刪除

//刪除,支持批量刪除  _schoolRepository.Delete(school);  await _unitOfWork.SaveChangesAsync();
//根據lambda刪除  _schoolRepository.Delete(x => x.Id == Id);  await _unitOfWork.SaveChangesAsync();

事務

//工作單元模式使用事務  await _schoolRepository.InsertAsync(school1);  await _schoolRepository.InsertAsync(school2);  await _unitOfWork.SaveChangesAsync();
//dapper使用事務  using (var tran = _unitOfWork.BeginTransaction())  {      try      {          await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",              school1,tran);          await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",              school2,tran);          tran.Commit();      }      catch (Exception e)      {          tran.Rollback();      }  }
//dapper+ef混合使用事務  using (var tran = _unitOfWork.BeginTransaction())  {      try      {          await _schoolRepository.InsertAsync(school1);          await _unitOfWork.SaveChangesAsync();            await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",              school2);          tran.Commit();      }      catch (Exception e)      {          tran.Rollback();      }  }

高級用法

//通過GetConnection可以使用更多dapper擴展的方法  await _unitOfWork.GetConnection().QueryAsync("select * from school");

寫在最後

Chimp核心是基於EF和Dapper的,所以EF和Dapper的功能都可以使用。比如導航屬性,字段映射等等。這個庫是線上項目核心依賴,會長期更新維護,希望大家能提出更好的意見。
QQ群:687800650 有問題可以加群交流

項目地址

數據庫腳本在根目錄的sqlscript文件夾裏面
github地址