2010-12-14 31 views
24

我正在玩最新的實體框架CTP 5版本,並構建了一個簡單的asp.net MVC博客,我只有兩個表格:Post和Comments。這完全是在POCO中完成的,我只需要DbContext部分的幫助,我需要它是單元可測試的(使用IDbSet?),我需要一個簡單/通用的存儲庫模式來添加,更新,刪除和檢索。任何幫助表示讚賞。實體框架4 CTP 4/CTP 5通用存儲庫模式和單元可測試

謝謝。

+1

它是一個良好的開端? http://romiller.com/2010/09/07/ef-ctp4-tips-tricks-testing-with-fake-dbcontext/ – Devart 2010-12-15 13:22:04

+0

我喜歡那個Devart,但它有點遠離通用存儲庫模式,我會喜歡查看採用單一類型的存儲庫。也許基於DBSet 而不是? – mxmissile 2010-12-15 15:23:27

回答

50

開始與你的DbContext,創建一個名爲Database.cs新文件:

Database.cs

public class Database : DbContext 
    { 

     private IDbSet<Post> _posts; 

     public IDbSet<Post> Posts { 
      get { return _posts ?? (_posts = DbSet<Post>()); } 
     } 

     public virtual IDbSet<T> DbSet<T>() where T : class { 
      return Set<T>(); 
     } 
     public virtual void Commit() { 
      base.SaveChanges(); 
     } 
} 

定義IDatabaseFactory與DatabaseFactory實現它:

IDatabaseFactory。 cs

public interface IDatabaseFactory : IDisposable 
    { 
     Database Get(); 
    } 

DatabaseFactory.cs

public class DatabaseFactory : Disposable, IDatabaseFactory { 
     private Database _database; 
     public Database Get() { 
      return _database ?? (_database = new Database()); 
     } 
     protected override void DisposeCore() { 
      if (_database != null) 
       _database.Dispose(); 
     } 
    } 

一次性擴展方法:

Disposable.cs

public class Disposable : IDisposable 
    { 
     private bool isDisposed; 

     ~Disposable() 
     { 
      Dispose(false); 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 
     private void Dispose(bool disposing) 
     { 
      if(!isDisposed && disposing) 
      { 
       DisposeCore(); 
      } 

      isDisposed = true; 
     } 

     protected virtual void DisposeCore() 
     { 
     } 
    } 

現在,我們可以定義我們的IRepository和我們RepositoryBase

個IRepository.cs

public interface IRepository<T> where T : class 
{ 
    void Add(T entity); 
    void Delete(T entity); 
    void Update(T entity); 
    T GetById(long Id); 
    IEnumerable<T> All(); 
    IEnumerable<T> AllReadOnly(); 
} 

RepositoryBase.cs

public abstract class RepositoryBase<T> where T : class 
    { 
     private Database _database; 
     private readonly IDbSet<T> _dbset; 
     protected RepositoryBase(IDatabaseFactory databaseFactory) 
     { 
      DatabaseFactory = databaseFactory; 
      _dbset = Database.Set<T>(); 
     } 

     protected IDatabaseFactory DatabaseFactory 
     { 
      get; private set; 
     } 

     protected Database Database 
     { 
      get { return _database ?? (_database = DatabaseFactory.Get()); } 
     } 
     public virtual void Add(T entity) 
     { 
      _dbset.Add(entity); 
     } 

     public virtual void Delete(T entity) 
     { 
      _dbset.Remove(entity); 
     } 

     public virtual void Update(T entity) 
     { 
      _database.Entry(entity).State = EntityState.Modified; 
     } 
     public virtual T GetById(long id) 
     { 
      return _dbset.Find(id); 
     } 

     public virtual IEnumerable<T> All() 
     { 
      return _dbset.ToList(); 
     } 
     public virtual IEnumerable<T> AllReadOnly() 
     { 
      return _dbset.AsNoTracking().ToList(); 
     } 
    } 

現在你可以創建你的IPostRepository和PostRepository:

IPostRepository.cs

 public interface IPostRepository : IRepository<Post> 
     { 
      //Add custom methods here if needed 
      Post ByTitle(string title); 
     } 

PostRepository.cs

public class PostRepository : RepositoryBase<Post>, IPostRepository 
     { 
      public PostRepository(IDatabaseFactory databaseFactory) : base(databaseFactory) 
      { 
      } 
      public Post ByTitle(string title) { 
       return base.Database.Posts.Single(x => x.Title == title); 
      } 
     } 

最後,UOW:

IUnitOfWork.cs

public interface IUnitOfWork 
{ 
    void Commit(); 
} 

的UnitOfWork。CS

private readonly IDatabaseFactory _databaseFactory; 
private Database _database; 

public UnitOfWork(IDatabaseFactory databaseFactory) 
{ 
    _databaseFactory = databaseFactory; 
} 

protected Database Database 
{ 
    get { return _database ?? (_database = _databaseFactory.Get()); } 
} 

public void Commit() 
{ 
    Database.Commit(); 
} 

使用在你的控制器:

private readonly IPostRepository _postRepository; 
private readonly IUnitOfWork_unitOfWork; 

     public PostController(IPostRepository postRepository, IUnitOfWork unitOfWork) 
     { 
      _postRepository = postRepository; 
      _unitOfWork = unitOfWork; 
     } 

     public ActionResult Add(Post post) { 
      _postRepository.Add(post); 
      _unitOfWork.Commit(); 
     } 

您將需要使用IoC容器像StructureMap,使這項工作。您可以通過NuGet安裝結構圖,或者如果您使用的是MVC 3,則可以安裝StructureMap-MVC NuGet包。 (鏈接下面)

Install-Package StructureMap.MVC4

Install-Package StructureMap.MVC3

Install-Package Structuremap

如果你有問題,只是讓我知道。希望能幫助到你。

+0

您使用過哪些SM生命週期? – mxmissile 2010-12-16 20:58:14

+3

你想使用HttpContextScoped。所以它看起來像:x.For ().HttpContextScoped()。使用(); etc – Paul 2010-12-16 21:55:39

+0

IDatabaseFactory的生命週期相同嗎? – mxmissile 2010-12-16 22:08:22

1

我唯一不同的做法是在實現中,即暴露服務層中的IPostRepository,並在控制器中有一個類型爲IPostService的接口字段,就像另一個抽象層一樣,否則這是一個很好的例子 - 好的,保羅。

+1

我在我的項目中也做了同樣的事情。這是一個很好的抽象,並保持控制器的動作乾淨和簡單。 – Paul 2011-01-12 01:37:25

+0

嗨保羅,這已經有一段時間了,你在這裏展示的功能非常好。但是我沒有做過任何單元測試:)就在今天我正在考慮做單元測試,並且使用MOQ框架來模擬數據,我該如何做呢?我是否必須重新創建倉庫作爲模擬倉庫,或者我可以重新使用我已有的倉庫?非常感謝你。 – Saxman 2011-01-18 17:16:30

+0

@Saxman - 我會問一個新的questoins。如何單元測試我的倉庫什麼的。我可以在那裏回答 – Paul 2011-01-18 22:44:32