2010-12-14 50 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