2011-11-14 36 views
1

我們開始在我們公司開發一個小框架,在不同的應用程序之間共享代碼。對於數據訪問,我們使用EF4。我們有一個自定義的DbContext類和通用存儲庫:實體框架 - 如何使用自定義DbContext實現存儲庫?

public class RMDbContext : DbContext 
{ 
    // .... 
} 

public interface IRepository 
{ 
    IQueryable<T> All(); 
    void Delete(T entity) where T : class; 
    void Add(T entity) where T : class; 
    void Update(T entity) where T : class; 
    int SaveChanges(); 
    void RollbackChanges(); 
} 

這裏的問題是如何實現資源庫,利用我們的自定義的DbContext類(RMDbContext)。我的同事認爲,最好的辦法就是讓RMDbContext實現IRepository接口:

public class RMDbContext : DbContext, IRepository 
{ 
    // .... 
} 

說實話,我不喜歡這種做法,因爲上下文被綁定到特定的合約(IRepository)。 IMO最好創建一個使用RMDbContext的存儲庫實現,如下所示:

public class Repository<T> : IRepository where T : RMDbContext, new() 
{ 
    protected readonly RMDbContext context; 

    public class Repository() 
    { 
     context = new T(); 
    } 

    // .... 
} 

您對這兩種方法有什麼看法?你會選擇哪一個,爲什麼?

+0

[EF4 CTP5版本庫模式]可能的重複版本(http://stackoverflow.com/questions/5066282/repository-pattern-with-ef4-ctp5) – jrummell

+0

這不是重複的那篇文章。我有2個場景,我想知道每種方法的優點和缺點:1)讓自定義DbContext實現IRepository,或2)創建一個單獨的類,實現IRepository接口並使用自定義的DbContext對象 –

回答

3

我們所做的工作是什麼來實現這樣的模式:

interface ICRUD<T> : ICreatable<T>, IRetrievable<T>, IUpdatable<T>, IDeletable<T> 
{ 
} 

interface ICreatable<T> 
{ 
    T Create(); 
} 

interface IRetrieve<T> 
{ 
    T Retrieve(params object[] keys); 
} 

interface IUpdatable<T> 
{ 
    void Update(T existing); 
} 

interface ICreatable<T> 
{ 
    void Delete(T existing); 
} 

然後我們創建了一個實體的供電基礎庫:

public abstract class BaseRepository<TModel, TEntities> where TEntities : IDbSet<TModel> 
{ 
    protected TEntities Entities {get; set;} 
    protected DbContext Db {get; set;} 

    public BaseRepository (DbContext db, TEntities entities) 
    { 
     Db = db; 
     Entities = entities; 
    } 

    public virtual TModel Create() { return Entities.Create(); } 
    public virtual TModel Retrieve (params object[] keys) { return Entities.Find (keys); } 
    public virtual void Update (TModel existing) { Db.Entry(existing).State = Modified; } 
    public virtual void Delete (TModel existing) { Db.Entry(existing).State = Removed; } 
} 

如果你注意到,在BaseRepository實際上並未使用ICRUD,只是有相同的方法簽名。由於我們對接口進行了編碼,這讓我們可以使用大量的共享代碼,而無需公開我們不想要的基類的功能。開發人員可以自由地實現數據存儲,但他們希望(例如,ICRUD可以與Web服務交談)不需要知道實體,或者他們也可以通過覆蓋任何提供的方法來自由地擴充BaseRepository提供的行爲,並且做不同的事情。

+0

這是....過度工程,恕我直言。無需爲每個CRUD操作創建合同 –

+1

它是如何被過度工程化的?我們創建了四個合同,ICreatable就是其中一個,並且感謝C#中的泛型,我們可以將其應用於我們的任何領域模型。它使我們能夠在9條線內旋轉一個DI'ed,完全可嘲弄的存儲庫,包括大括號。 –

+0

我只是不認爲有必要創建4個合同,我會創建只有一個: '接口ICRUD { 牛逼的Create(); T Retrieve(params object [] keys); 無效更新(T存在); void刪除(T存在); } ' –

4

我個人鼓勵你們不要創建任何東西,只要使用dbContext,它擁有你需要的所有方法。

我自己實現了#1(實現IRepository),但是你最終會做一些時髦的編程來到正確的ObjectSet或EntitySet添加或刪除添加/刪除方法。

隨着您在objectmodel中添加繼承層次結構,該代碼會變得越來越複雜。

+1

只需使用DbContext一個壞主意,因爲它可能違反幹。這也是非常不可測的。 –

+1

我同意可測試性,但好奇它是如何違反DRY –

+0

關閉我的頭頂,只對當前登錄的用戶操作。標準最初可能是'Where(user => user.IsLoggedIn)',但可能會移到'Where(user => user.LastLogin <= DateTime.Now && user.LastLogout

2

首先,您不應該將存儲庫與RMDbContext緊密結合,因爲這是一種糟糕的設計氣味,您應該始終使用接口。

其次,存儲庫不應該實現IRMDbContext接口(如果存在,因爲存儲庫不需要它)。您應該使用它,而不是實現它,所以如果你創建你的版本庫採取IRMDbContext的參數在他的構造函數如下

公共類資源庫 {

protected readonly IRMDbContext context; 

public class Repository(IRMDbContext rMDbContext) 
{ 
    this.context = rMDbContext; 
} 

和工作類的單位是更好該實例化的背景下,並將其發送到資源庫中一個請訪問以下鏈接

Unit Of Work With EF

2

EF Repositry最佳實踐...
實體框架 - 代碼優先模型方法,爲數據庫表創建POCO實體。
這個模型可以使用WCF數據契約或自定義屬性。
這是一個使用此我們可以添加另一個倉庫以及實現在未來也使用依賴注入

IRepository接口

/// <summary> 
/// Repository 
/// </summary> 
public partial interface IRepository<T> where T : BaseEntity 
{ 
    /// <summary> 
    /// Returns the queryable entity set for the given type {T}. 
    /// </summary> 
    IQueryable<T> Table { get; } 

    /// <summary> 
    /// Creates a new instance of an entity of type {T} 
    /// </summary> 
    /// <returns>The new entity instance.</returns> 
    T Create(); 

    /// <summary> 
    /// Gets an entity by id from the database or the local change tracker. 
    /// </summary> 
    /// <param name="id">The id of the entity. This can also be a composite key.</param> 
    /// <returns>The resolved entity</returns> 
    T GetById(object id); 

    /// <summary> 
    /// Marks the entity instance to be saved to the store. 
    /// </summary> 
    /// <param name="entity">An entity instance that should be saved to the database.</param> 
    /// <remarks>Implementors should delegate this to the current <see cref="IDbContext" /></remarks> 
    void Insert(T entity); 

    /// <summary> 
    /// Marks multiple entities to be saved to the store. 
    /// </summary> 
    /// <param name="entities">The list of entity instances to be saved to the database</param> 
    /// <param name="batchSize">The number of entities to insert before saving to the database (if <see cref="AutoCommitEnabled"/> is true)</param> 
    void InsertRange(IEnumerable<T> entities, int batchSize = 100); 

    /// <summary> 
    /// Marks the changes of an existing entity to be saved to the store. 
    /// </summary> 
    /// <param name="entity">An instance that should be updated in the database.</param> 
    /// <remarks>Implementors should delegate this to the current <see cref="IDbContext" /></remarks> 
    void Update(T entity); 

    /// <summary> 
    /// Marks an existing entity to be deleted from the store. 
    /// </summary> 
    /// <param name="entity">An entity instance that should be deleted from the database.</param> 
    /// <remarks>Implementors should delegate this to the current <see cref="IDbContext" /></remarks> 
    void Delete(T entity); 


    /// <summary> 
    /// Returns the data context associated with the repository. 
    /// </summary> 
    /// <remarks> 
    /// The context is likely shared among multiple repository types. 
    /// So committing data or changing configuration also affects other repositories. 
    /// </remarks> 
    IDbContext Context { get; } 

    /// <summary> 
    /// Gets or sets a value indicating whether database write operations 
    /// such as insert, delete or update should be committed immediately. 
    /// </summary> 
    bool AutoCommitEnabled { get; set; } 
} 

實施

/// <summary> 
/// Entity Framework repository 
/// </summary> 
public partial class EfRepository<T> : IRepository<T> where T : BaseEntity 
{ 

    #region Fields 

    private readonly IDbContext _context; 
    private IDbSet<T> _entities; 

    #endregion 

    #region Ctor 

    public EfRepository(IDbContext context) 
    { 
     this._context = context; 
     this.AutoCommitEnabled = true; 
    } 

    #endregion 

    #region interface members 

    public virtual IQueryable<T> Table 
    { 
     get 
     { 
      return this.Entities; 
     } 
    } 

    public T Create() 
    { 
     return this.Entities.Create(); 
    } 

    public T GetById(object id) 
    { 
     return this.Entities.Find(id); 
    } 

    public void Insert(T entity) 
    { 
     if (entity == null) 
      throw new ArgumentNullException("entity"); 

     this.Entities.Add(entity); 

     if (this.AutoCommitEnabled) 
      _context.SaveChanges(); 
    } 

    public void InsertRange(IEnumerable<T> entities, int batchSize = 100) 
    { 
     try 
     { 
      if (entities == null) 
       throw new ArgumentNullException("entities"); 

      if (entities.HasItems()) 
      { 
       if (batchSize <= 0) 
       { 
        // insert all in one step 
        entities.Each(x => this.Entities.Add(x)); 
        if (this.AutoCommitEnabled) 
         _context.SaveChanges(); 
       } 
       else 
       { 
        int i = 1; 
        bool saved = false; 
        foreach (var entity in entities) 
        { 
         this.Entities.Add(entity); 
         saved = false; 
         if (i % batchSize == 0) 
         { 
          if (this.AutoCommitEnabled) 
           _context.SaveChanges(); 
          i = 0; 
          saved = true; 
         } 
         i++; 
        } 

        if (!saved) 
        { 
         if (this.AutoCommitEnabled) 
          _context.SaveChanges(); 
        } 
       } 
      } 
     } 
     catch (DbEntityValidationException ex) 
     { 
      throw ex; 
     } 
    } 

    public void Update(T entity) 
    { 
     if (entity == null) 
      throw new ArgumentNullException("entity"); 

     if (this.AutoCommitEnabled) 
     { 
      _context.SaveChanges(); 
     } 
     else 
     { 
      try 
      { 
       this.Entities.Attach(entity); 
       InternalContext.Entry(entity).State = System.Data.EntityState.Modified; 
      } 
      finally { } 
     } 
    } 

    public void Delete(T entity) 
    { 
     if (entity == null) 
      throw new ArgumentNullException("entity"); 

     if (InternalContext.Entry(entity).State == System.Data.EntityState.Detached) 
     { 
      this.Entities.Attach(entity); 
     } 

     this.Entities.Remove(entity); 

     if (this.AutoCommitEnabled) 
      _context.SaveChanges(); 
    } 


    public IDbContext Context 
    { 
     get { return _context; } 
    } 

    public bool AutoCommitEnabled { get; set; } 

    #endregion 

    #region Helpers 

    protected internal ObjectContextBase InternalContext 
    { 
     get { return _context as ObjectContextBase; } 
    } 

    private DbSet<T> Entities 
    { 
     get 
     { 
      if (_entities == null) 
      { 
       _entities = _context.Set<T>(); 
      } 
      return _entities as DbSet<T>; 
     } 
    } 

    #endregion 

} 

我迷人的選項。 這不會影響任何現有的數據。 試一試.....