自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

倉儲(chǔ)模式到底是不是反模式?

存儲(chǔ) 存儲(chǔ)軟件
倉儲(chǔ)模式我們已耳熟能詳,但當(dāng)我們將其進(jìn)行應(yīng)用時(shí),真的是那么得心應(yīng)手嗎?確定是解放了生產(chǎn)力嗎?這到底是怎樣的一個(gè)存在,確定不是反模式?

 [[374908]]

本文轉(zhuǎn)載自微信公眾號(hào)「JeffckyShare」,作者Jeffcky。轉(zhuǎn)載本文請(qǐng)聯(lián)系JeffckyShare公眾號(hào)。   

倉儲(chǔ)反模式

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

定義通用IRepository接口

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

當(dāng)然還有泛型類可能需要基礎(chǔ)子基礎(chǔ)類等等,這里我們一并忽略

 定義EntityRepository實(shí)現(xiàn)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獲取實(shí)體 
  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.     } 

定義業(yè)務(wù)倉儲(chǔ)接口IUserRepository接口

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

 定義業(yè)務(wù)倉儲(chǔ)接口具體實(shí)現(xiàn)UserRepository

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

我們定義基礎(chǔ)通用接口和實(shí)現(xiàn),然后每一個(gè)業(yè)務(wù)都定義一個(gè)倉儲(chǔ)接口和實(shí)現(xiàn),最后將其進(jìn)行注入,如下:

  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>()); 

有一部分童鞋在項(xiàng)目中可能就是使用如上方式,每一個(gè)具體倉儲(chǔ)實(shí)現(xiàn)我們將其看成傳統(tǒng)的數(shù)據(jù)訪問層,緊接著我們還定義一套業(yè)務(wù)層即服務(wù)層,如此第一眼看來和傳統(tǒng)三層架構(gòu)無任何區(qū)別,只是分層名稱有所不同而已

每一個(gè)具體倉儲(chǔ)接口都繼承基礎(chǔ)倉儲(chǔ)接口,然后每個(gè)具體倉儲(chǔ)實(shí)現(xiàn)繼承基礎(chǔ)倉儲(chǔ)實(shí)現(xiàn),對(duì)于服務(wù)層同理,反觀上述一系列操作本質(zhì),其實(shí)我們回到了原點(diǎn),那還不如直接通過上下文操作一步到位來的爽快

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

倉儲(chǔ)模式思考

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

根據(jù)如下官方在微服務(wù)所使用倉儲(chǔ)鏈接,官方推崇倉儲(chǔ)模式,但在其鏈接中是直接在具體倉儲(chǔ)實(shí)現(xiàn)中所使用上下文進(jìn)行操作,毫無以為這沒半點(diǎn)毛病

EntityFramework Core基礎(chǔ)設(shè)施持久化層

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

但我們想在上下文的基礎(chǔ)上進(jìn)一步將基本增、刪、改、查詢進(jìn)行封裝,那么我們?nèi)绾畏庋b基礎(chǔ)倉儲(chǔ)而避免出現(xiàn)反模式呢?

我思倉儲(chǔ)模式

在進(jìn)行改造之前,我們思考兩個(gè)潛在需要解決的重點(diǎn)問題

其一,每一個(gè)具體業(yè)務(wù)倉儲(chǔ)實(shí)現(xiàn),定義倉儲(chǔ)接口是一定必要的嗎?我認(rèn)為完全沒必要,有的童鞋就疑惑了,若我們有非封裝基礎(chǔ)通用接口,需額外定義,那怎么搞,我們可以基于基礎(chǔ)倉儲(chǔ)接口定義擴(kuò)展方法

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

其三,若非常復(fù)雜的查詢,可通過底層連接實(shí)現(xiàn)或引入Dapper

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

  1. /// <summary> 
  2. /// 基礎(chǔ)通用接口 
  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); 

上述我們將基礎(chǔ)倉儲(chǔ)接口具體實(shí)現(xiàn)類,將其定義為抽象,既然我們封裝了針對(duì)基礎(chǔ)倉儲(chǔ)接口的實(shí)現(xiàn),外部只需調(diào)用即可,那么該類理論上就不應(yīng)該被繼承,所以接下來我們將其修飾為密封類,如下:

  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.     } 

我們從容器中獲取上下文并進(jìn)一步暴露上下文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.     } 

若為基礎(chǔ)倉儲(chǔ)接口不滿足實(shí)現(xiàn),則使用具體倉儲(chǔ)的擴(kuò)展方法

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

最后到了服務(wù)層,則是我們的業(yè)務(wù)層,我們只需要使用上述基礎(chǔ)倉儲(chǔ)接口或擴(kuò)展方法即可

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

最后在注入時(shí),我們將省去注冊(cè)每一個(gè)具體倉儲(chǔ)實(shí)現(xiàn),如下:

  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>(); 

以上只是針對(duì)第一種反模式的基本改造,對(duì)于UnitOfWork同理,其本質(zhì)不過是管理操作事務(wù),并需我們手動(dòng)管理上下文釋放時(shí)機(jī)就好,這里就不再多講

我們還可以根據(jù)項(xiàng)目情況可進(jìn)一步實(shí)現(xiàn)其對(duì)應(yīng)規(guī)則,比如在是否需要在進(jìn)行指定操作之前實(shí)現(xiàn)自定義擴(kuò)展,比如再抽取一個(gè)上下文接口等等,ABP vNext中則是如此,ABP vNext對(duì)EF Core擴(kuò)展是我看過最完美的實(shí)現(xiàn)方案,接下來我們來看看

ABP vNext倉儲(chǔ)模式

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

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

在其EF Core包中的AbpDbContext上下文中,針對(duì)屬性跟蹤更改做了良好的實(shí)現(xiàn),如下:

  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.     ..... 

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

  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(); 

額外的還實(shí)現(xiàn)了基于環(huán)境流動(dòng)的事務(wù)(AmbientUnitOfWork),反正ABP vNext在EF Core這塊擴(kuò)展實(shí)現(xiàn)令人嘆服,我也在持續(xù)學(xué)習(xí)中,其他就不多講了,博客園中講解原理的文章比比皆是

 

責(zé)任編輯:武曉燕 來源: JeffckyShare
相關(guān)推薦

2012-07-02 09:40:45

小米手機(jī)

2019-06-06 08:30:07

區(qū)塊鏈數(shù)字貨幣比特幣

2020-02-25 16:30:36

MD5是不是加密

2019-02-27 09:28:15

Redis服務(wù)器事務(wù)

2024-07-05 09:00:00

編程語言Rust開發(fā)

2021-08-02 14:48:15

云電腦Windows 365華為

2024-02-07 12:35:00

React并發(fā)模式concurrent

2016-12-23 09:04:56

大數(shù)據(jù)技術(shù)BAT

2020-10-21 10:54:07

物聯(lián)網(wǎng)商業(yè)技術(shù)

2019-12-16 09:42:38

PHP語言程序員

2021-04-13 10:35:13

網(wǎng)盤存儲(chǔ)硬盤

2021-03-10 13:42:27

筆記本雙屏設(shè)計(jì)

2023-10-30 18:59:38

REST API開發(fā)

2017-09-07 14:44:10

程序員

2024-09-23 08:30:48

2023-09-13 11:58:17

云原生反模式

2021-01-11 13:32:14

比特幣加密貨幣區(qū)塊鏈

2011-05-24 09:30:26

Findbugs

2020-07-14 16:22:50

滴滴自動(dòng)駕駛傳感器

2020-06-17 07:37:35

5G4G應(yīng)用
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)