2016-08-09 80 views
2

我想在我的應用程序中實現最小的通用存儲庫模式。我有查詢和保存數據一個非常小的接口:使用實體框架的最小存儲庫實現

public interface IRepository 
{ 
    IQueryable<TEntity> Query<TEntity>() 
     where TEntity: BaseEntity; 

    void Save<TEntity>(TEntity entity) 
     where TEntity : BaseEntity; 
} 

BaseEntity是我將在我的倉庫儲存的所有對象的基類:

public abstract class BaseEntity 
{ 
    public Guid Id { get; set; }  
    public DateTime CreatedDate { get; set; } 
    public DateTime UpdatedDate { get; set; } 
} 

我試圖找到一個工作實現使用實體框架這樣一個簡單的存儲庫,但是很難找到(人們使用UnitOfWork和其他使實現比我想要的更復雜的東西)。

因此,我創建了絕對最小的實現我能想出:

public class EfRepository : DbContext, IRepository 
{ 
    public IQueryable<TEntity> Query<TEntity>() where TEntity : BaseEntity 
    { 
     return this.Set<TEntity>(); 
    } 

    public void Save<TEntity>(TEntity entity) where TEntity : BaseEntity 
    { 
     if (entity.Id == default(Guid)) 
     { 
      entity.Id = Guid.NewGuid(); 
      this.Set<TEntity>().Add(entity); 
     } 
     else 
     { 
      this.Entry(entity).State = EntityState.Modified; 
     }  

     this.SaveChanges(); 
    } 

    public DbSet<User> Users { get; set; } // User is a subclass of BaseEntity 
    //Other DbSet's... 
} 

現在,我的問題是,如果這樣的實現方式是正確的。我在問,因爲我是Entity Framework的新手,並且擔心可能出現的性能問題或使用此類存儲庫時可能出錯的事情。

注:我試圖做這一切的原因有二:

  • 出於測試目的,這樣我可以在我的單元測試創​​建存儲庫的模擬項目
  • 這可能是我將來必須切換到另一個ORM,並且我希望儘可能簡化這個過渡。
+0

如果您是在輕觸後出現性能問題,EF是否適合使用,可能值得考慮。從個人經驗來看,我還沒有發現EF在我使用過的任何項目中都表現得非常好,並且它有點笨拙,使它的映射符合數據庫或代碼(取決於您選擇的路徑,代碼或DB首先)。 我認識的任何人在高吞吐量環境中工作時,只要有人提到EF,就會身體上發生變化。 –

+5

問題的確是:因爲EF *已經*實現了庫('DbSet ')和工作單元('DbContext')模式 - 爲什麼重新發明了另一個層面的輪子? –

+0

@marc_s,我有2個理由:)請參閱我的編輯 –

回答

2

首先存儲庫是有爭議的。有很多人強烈反對,並且由於各種原因大量使用它(或習慣了它?)。在互聯網上有許多關於利弊的無盡討論。您需要決定是否實際需要項目中的存儲庫模式 - 當您問到「如何在C#中執行此操作?」時,我們不會關注它嗎?不是「我應該那樣做嗎?」。

您的存儲庫實現擴展爲DbContext。這意味着您無法有效創建跨多個存儲庫(多個實體類型)的事務,因爲每個存儲庫都有自己的DbContext(因爲它是上下文)。在引擎蓋下DbContext跟蹤對實體所做的更改。如果你有多個上下文並且試圖同時保存兩者,他們將不會彼此瞭解。這給我們帶來了一個問題 - 如果第一次調用SaveChanges()成功,第二次失敗如何回滾第一個?它已經救了?這就是工作單位出現的地方。

所以首先你需要一個倉庫接口 - 作爲實體的集合:

public interface IRepository<TEntity> 
{ 
    TEntity Get(Expression<Func<TEntity, bool>> predicate); 
    IEnumerable<TEntity> GetAll(); 
    IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate); 

    void Add(TEntity entity); 
    void AddAll(IEnumerable<TEntity> entities); 

    void Remove(TEntity entity); 
    void RemoveAll(IEnumerable<TEntity> entities); 
} 

和單位的工作:

public interface IUnitOfWork : IDisposable 
{ 
    // Commit all the changes 
    void Complete(); 

    // Concrete implementation -> IRepository<Foo> 
    // Add all your repositories here: 
    IFooRepository Foos {get;} 
} 

基類可以看看如下:

public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class 
{ 
    protected DbContext Context { get; private set; } 

    public BaseRepository(DbContext dbContext) 
    { 
     Context = dbContext; 
    } 

    public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate) 
    { 
     return Context.Set<TEntity>().Where(predicate).FirstOrDefault(); 
    } 

    public virtual IEnumerable<TEntity> GetAll() 
    { 
     return Context.Set<TEntity>().ToList(); 
    } 

    public virtual IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate) 
    { 
     return Context.Set<TEntity>().Where(predicate).ToList(); 
    } 

    public void Add(TEntity entity) 
    { 
     var entry = Context.Entry(entity); 
     if(entry.State == EntityState.Detached) 
     { 
      Context.Set<TEntity>().Add(entity); 
     } 
     else 
     { 
      entry.State = EntityState.Modified; 
     } 
    } 

    public void AddAll(IEnumerable<TEntity> entities) 
    { 
     foreach(var entity in entities) 
     { 
      Add(entity); 
     } 
    } 

    public void Remove(TEntity entity) 
    { 
     var entry = Context.Entry(entity); 
     if (entry.State == EntityState.Detached) 
     { 
      Context.Set<TEntity>().Attach(entity); 
     } 
     Context.Entry<TEntity>(entity).State = EntityState.Deleted; 
    } 

    public void RemoveAll(IEnumerable<TEntity> entities) 
    { 
     foreach (var entity in entities) 
     { 
      Remove(entity); 
     } 
    } 

} 

和工作單元執行:

public class UnitOfWork : IUnitOfWork 
{ 
    private readonly ApplicationDbContext _dbContext; 
    private IFooRepository _fooRepo; 

    public UnitOfWork(ApplicationDbContext dbContext) 
    { 
     _dbContext = dbContext; 

     // Each repo will share the db context: 
     _fooRepo = new FooRepository(_dbContext); 
    } 


    public IFooRepository Foos 
    { 
     get 
     { 
      return _fooRepo; 
     } 
    } 

    public void Complete() 
    { 
     _dbContext.SaveChanges(); 
    } 

    public void Dispose() 
    { 
     _dbContext.Dispose(); 
    } 
} 
+0

_you無法有效地創建一個跨越多個存儲庫的事務_--這並不完全真正。我可以創建一個'TransactionScope'並將其包裝在所有必要的存儲庫調用中。默認情況下(沒有事務範圍),我的存儲庫實現將在每次調用Save時進行所有更改,並且這種默認行爲是我在大多數情況下需要的。 –

+0

您的'Save()'會使用相同的'DbContext'從所有存儲庫中獲取所有修改的實體,而不管您將調用哪個回購。 –