成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

倉儲模式到底是不是反模式?

存儲 存儲軟件
倉儲模式我們已耳熟能詳,但當我們將其進行應用時,真的是那么得心應手嗎?確定是解放了生產力嗎?這到底是怎樣的一個存在,確定不是反模式?

 [[374908]]

本文轉載自微信公眾號「JeffckyShare」,作者Jeffcky。轉載本文請聯系JeffckyShare公眾號。   

倉儲反模式

5年前我在Web APi中使用EntityFramework中寫了一個倉儲模式,并將其放在我個人github上,此種模式也完全是參考所流行的網傳模式,現如今在我看來那是極其錯誤的倉儲模式形式,當時在EntityFramework中有IDbSet接口,然后我們又定義一個IDbContext接口等等,大同小異,接下來我們看看在.NET Core中大多是如何使用的呢?

定義通用IRepository接口

  1. public interface IRepository<TEntity> where TEntity : class 
  2.     /// <summary> 
  3.     /// 通過id獲得實體 
  4.     /// </summary> 
  5.     /// <param name="id"></param> 
  6.     /// <returns></returns
  7.     TEntity GetById(object id); 
  8.      
  9.     //其他諸如修改、刪除、查詢接口 

當然還有泛型類可能需要基礎子基礎類等等,這里我們一并忽略

 定義EntityRepository實現IRepository接口

  1. public abstract class EntityRepository<TEntity> : IRepository<TEntity> where TEntity : class 
  2.     private readonly DbContext _context; 
  3.  
  4.     public EntityRepository(DbContext context) 
  5.     { 
  6.         _context = context; 
  7.     } 
  8.  
  9.     /// <summary> 
  10.     /// 通過id獲取實體 
  11.     /// </summary> 
  12.     /// <param name="id"></param> 
  13.     /// <returns></returns
  14.     public TEntity GetById(object id) 
  15.     { 
  16.         return _context.Set<TEntity>().Find(id); 
  17.     } 

定義業務倉儲接口IUserRepository接口

  1. public interface IUserRepository : IRepository<User
  2.     /// <summary> 
  3.     /// 其他非通用接口 
  4.     /// </summary> 
  5.     /// <returns></returns
  6.     List<User> Other(); 

 定義業務倉儲接口具體實現UserRepository

  1. public class UserRepository : EntityRepository<User>, IUserRepository 
  2.     public List<User> Other() 
  3.     { 
  4.         throw new NotImplementedException(); 
  5.     } 

我們定義基礎通用接口和實現,然后每一個業務都定義一個倉儲接口和實現,最后將其進行注入,如下:

  1. services.AddDbContext<EFCoreDbContext>(options => 
  2.     options.UseSqlServer(@"Server=.;Database=EFCore;Trusted_Connection=True;"); 
  3. }); 
  4.  
  5. services.AddScoped(typeof(IRepository<>), typeof(EntityRepository<>)); 
  6.  
  7. services.AddScoped<IUserRepository, UserRepository>(); 
  8.  
  9. services.AddScoped<IUserService, UserService>()); 

有一部分童鞋在項目中可能就是使用如上方式,每一個具體倉儲實現我們將其看成傳統的數據訪問層,緊接著我們還定義一套業務層即服務層,如此第一眼看來和傳統三層架構無任何區別,只是分層名稱有所不同而已

每一個具體倉儲接口都繼承基礎倉儲接口,然后每個具體倉儲實現繼承基礎倉儲實現,對于服務層同理,反觀上述一系列操作本質,其實我們回到了原點,那還不如直接通過上下文操作一步到位來的爽快

上述倉儲模式并沒有帶來任何益處,分層明確性從而加大了復雜性和重復性,根本沒有解放生產率,我們將專注力全部放在了定義多層接口和實現上而不是業務邏輯,如此使用,這就是倉儲模式的反模式實現

倉儲模式思考

所有脫離實際項目和業務的思考都是耍流氓,若只是小型項目,直接通過上下文操作未嘗不可,既然用到了倉儲模式說明是想從一定程度上解決項目中所遇到的痛點所在,要不然只是隨波逐流,終將是自我打臉

根據如下官方在微服務所使用倉儲鏈接,官方推崇倉儲模式,但在其鏈接中是直接在具體倉儲實現中所使用上下文進行操作,毫無以為這沒半點毛病

EntityFramework Core基礎設施持久化層

https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-implementation-entity-framework-core

但我們想在上下文的基礎上進一步將基本增、刪、改、查詢進行封裝,那么我們如何封裝基礎倉儲而避免出現反模式呢?

我思倉儲模式

在進行改造之前,我們思考兩個潛在需要解決的重點問題

其一,每一個具體業務倉儲實現,定義倉儲接口是一定必要的嗎?我認為完全沒必要,有的童鞋就疑惑了,若我們有非封裝基礎通用接口,需額外定義,那怎么搞,我們可以基于基礎倉儲接口定義擴展方法

其二,若與其他倉儲進行互操作,此時基礎倉儲不滿足需求,那怎么搞,我們可以在基礎倉儲接口中定義暴露獲取上下文Set屬性

其三,若非常復雜的查詢,可通過底層連接實現或引入Dapper

首先,我們保持上述封裝基礎倉儲接口前提下添加暴露上下文Set屬性,如下:

  1. /// <summary> 
  2. /// 基礎通用接口 
  3. /// </summary> 
  4. /// <typeparam name="TEntity"></typeparam> 
  5. public interface IRepository<T> where T : class 
  6.     IQueryable<T> Queryable { get; } 
  7.     T GetById(object id); 

上述我們將基礎倉儲接口具體實現類,將其定義為抽象,既然我們封裝了針對基礎倉儲接口的實現,外部只需調用即可,那么該類理論上就不應該被繼承,所以接下來我們將其修飾為密封類,如下:

  1. public sealed class EntityRepository<T> : IRepository<T> where T : class 
  2.     private readonly DbContext _context; 
  3.  
  4.     public EntityRepository(DbContext context) 
  5.     { 
  6.         _context = context; 
  7.     } 
  8.  
  9.     public T GetById(object id) 
  10.     { 
  11.         return _context.Set<T>().Find(id); 
  12.     } 

我們從容器中獲取上下文并進一步暴露上下文Set屬性

  1. public sealed class EntityRepository<T> : IRepository<T> where T : class 
  2.     private readonly IServiceProvider _serviceProvider; 
  3.  
  4.     private EFCoreDbContext _context => (EFCoreDbContext) 
  5.         _serviceProvider.GetService(typeof(EFCoreDbContext)); 
  6.  
  7.     private DbSet<T> Set => _context.Set<T>(); 
  8.  
  9.     public IQueryable<T> Queryable => Set
  10.  
  11.     public EntityRepository(IServiceProvider serviceProvider) 
  12.     { 
  13.         _serviceProvider = serviceProvider; 
  14.     } 
  15.  
  16.     public T GetById(object id) 
  17.     { 
  18.         return Set.Find(id); 
  19.     } 

若為基礎倉儲接口不滿足實現,則使用具體倉儲的擴展方法

  1. public static class UserRepository 
  2.     public static List<User> Other(this IRepository<User> repository) 
  3.     { 
  4.         // 自定義其他實現 
  5.     } 

最后到了服務層,則是我們的業務層,我們只需要使用上述基礎倉儲接口或擴展方法即可

  1. public class UserService 
  2.     private readonly IRepository<User> _repository; 
  3.     public UserService(IRepository<User>  repository) 
  4.     { 
  5.         _repository = repository; 
  6.     } 

最后在注入時,我們將省去注冊每一個具體倉儲實現,如下:

  1. services.AddDbContext<EFCoreDbContext>(options => 
  2.     options.UseSqlServer(@"Server=.;Database=EFCore;Trusted_Connection=True;"); 
  3. }); 
  4.  
  5. services.AddScoped(typeof(IRepository<>), typeof(EntityRepository<>)); 
  6.  
  7. services.AddScoped<UserService>(); 

以上只是針對第一種反模式的基本改造,對于UnitOfWork同理,其本質不過是管理操作事務,并需我們手動管理上下文釋放時機就好,這里就不再多講

我們還可以根據項目情況可進一步實現其對應規則,比如在是否需要在進行指定操作之前實現自定義擴展,比如再抽取一個上下文接口等等,ABP vNext中則是如此,ABP vNext對EF Core擴展是我看過最完美的實現方案,接下來我們來看看

ABP vNext倉儲模式

其核心在Volo.Abp.EntityFrameworkCore包中,將其單獨剝離出來除了抽象通用封裝外,還有一個則是調用了EF Core底層APi,一旦EF Core版本變動,此包也需同步更新

ABP vNext針對EF Core做了擴展,通過查看整體實現,主要通過擴展中特性實現指定屬性更新,EF Core中當模型被跟蹤時,直接提交則更新變化屬性,若未跟蹤,我們直接Update但想要更新指定屬性,這種方式不可行,在ABP vNext則得到了良好的解決

在其EF Core包中的AbpDbContext上下文中,針對屬性跟蹤更改做了良好的實現,如下:

  1. protected virtual void ChangeTracker_Tracked(object sender, EntityTrackedEventArgs e) 
  2.     FillExtraPropertiesForTrackedEntities(e); 
  3.  
  4. protected virtual void FillExtraPropertiesForTrackedEntities(EntityTrackedEventArgs e) 
  5.     var entityType = e.Entry.Metadata.ClrType; 
  6.     if (entityType == null
  7.     { 
  8.         return
  9.     } 
  10.  
  11.     if (!(e.Entry.Entity is IHasExtraProperties entity)) 
  12.     { 
  13.         return
  14.     } 
  15.      
  16.     ..... 

除此之外的第二大亮點則是對UnitOfWork(工作單元)的完美方案,將其封裝在Volo.Abp.Uow包中,通過UnitOfWorkManager管理UnitOfWork,其事務提交不簡單是像如下形式

  1. private IDbContextTransaction _transaction;  
  2. public void BeginTransaction() 
  3. {  
  4.     _transaction = Database.BeginTransaction(); 
  5.  
  6. public void Commit() 
  7.     try 
  8.     { 
  9.         SaveChanges(); 
  10.         _transaction.Commit(); 
  11.     } 
  12.     finally 
  13.     { 
  14.         _transaction.Dispose(); 
  15.     }         
  16.  
  17. public void Rollback() 
  18. {  
  19.     _transaction.Rollback(); 
  20.     _transaction.Dispose(); 

額外的還實現了基于環境流動的事務(AmbientUnitOfWork),反正ABP vNext在EF Core這塊擴展實現令人嘆服,我也在持續學習中,其他就不多講了,博客園中講解原理的文章比比皆是

 

責任編輯:武曉燕 來源: JeffckyShare
相關推薦

2012-07-02 09:40:45

小米手機

2019-06-06 08:30:07

區塊鏈數字貨幣比特幣

2020-02-25 16:30:36

MD5是不是加密

2019-02-27 09:28:15

Redis服務器事務

2024-07-05 09:00:00

編程語言Rust開發

2021-08-02 14:48:15

云電腦Windows 365華為

2024-02-07 12:35:00

React并發模式concurrent

2016-12-23 09:04:56

大數據技術BAT

2020-10-21 10:54:07

物聯網商業技術

2019-12-16 09:42:38

PHP語言程序員

2021-04-13 10:35:13

網盤存儲硬盤

2021-03-10 13:42:27

筆記本雙屏設計

2023-10-30 18:59:38

REST API開發

2017-09-07 14:44:10

程序員

2021-01-11 13:32:14

比特幣加密貨幣區塊鏈

2024-09-23 08:30:48

2020-07-14 16:22:50

滴滴自動駕駛傳感器

2023-09-13 11:58:17

云原生反模式

2011-05-24 09:30:26

Findbugs

2020-06-17 07:37:35

5G4G應用
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 黄色片在线网站 | 国产成人免费在线观看 | 羞羞的视频免费看 | 夜夜爽99久久国产综合精品女不卡 | 一级久久久久久 | 一级在线毛片 | 日本一二三区在线观看 | 日韩中文字幕第一页 | 久久久久久久久久久一区二区 | 午夜影院在线观看 | 成人免费在线观看 | av无遮挡 | 欧美日韩成人影院 | 国产免费让你躁在线视频 | 天堂久久天堂综合色 | 狠狠草视频 | 亚洲人va欧美va人人爽 | www.国产91| 国产一区二区精品 | 亚洲成人一区 | 亚洲 中文 欧美 日韩 在线观看 | 欧美在线视频观看 | 欧美高清hd | 国产第二页| 精品视频国产 | 一区二区精品视频 | 亚洲精品一区二区三区中文字幕 | 99re视频在线观看 | 亚洲在线电影 | 欧美一级免费看 | 国产精品一区二区欧美黑人喷潮水 | 网黄在线 | 国产高清免费视频 | 男人天堂久久久 | 亚洲一区二区视频在线观看 | 久久精点视频 | 二区三区视频 | 伊伊综合网 | 亚洲人成网站777色婷婷 | 欧美片网站免费 | 国产一区二区三区在线视频 |