【问题标题】:Entity Framework 4 CTP 4 / CTP 5 Generic Repository Pattern and Unit TestableEntity Framework 4 CTP 4 / CTP 5 通用存储库模式和单元可测试
【发布时间】:2010-12-14 18:34:23
【问题描述】:

我正在使用最新的 Entity Framework CTP 5 版本并构建一个简单的 asp.net MVC 博客,其中只有两个表:Post 和 Comments。这完全在 POCO 中完成,我只需要 DbContext 部分的帮助,我需要它可以进行单元测试(使用 IDbSet?),并且我需要一个简单/通用的存储库模式来添加、更新、删除、检索。任何帮助表示赞赏。

谢谢。

【问题讨论】:

  • 这是一个好的第一步吗? romiller.com/2010/09/07/…
  • 我喜欢 Devart,但它有点偏离通用存储库模式,我希望看到一个采用单一类型的存储库。也许基于 DBSet 而不是?

标签: asp.net-mvc-2 entity-framework-4 repository-pattern


【解决方案1】:

从你的 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();
        }

您需要使用像 StructureMap 这样的 IoC 容器来完成这项工作。您可以通过 NuGet 安装结构映射,或者如果您使用的是 MVC 3,则可以安装 StructureMap-MVC NuGet 包。 (以下链接)

Install-Package StructureMap.MVC4

Install-Package StructureMap.MVC3

Install-Package Structuremap

如果您有任何问题,请告诉我。希望对您有所帮助。

【讨论】:

  • 您为每个这些使用了哪个 SM 生命周期?
  • 你想使用 HttpContextScoped。所以它看起来像: x.For().HttpContextScoped().Use();等
  • IDatabaseFactory 的生命周期相同?
  • 是的,您希望所有数据访问问题都是 HttpContextScoped。包括任何 I...Repository()
  • 嗨,Paul,我对 RepositoryBase 类有一点问题,对于 Add/Delete 等...方法...这段代码来自哪里:Check.Argument.IsNotNull?我实际上正在为 IoC 使用 StructureMap。谢谢。
【解决方案2】:

我只是喜欢这篇关于 Entity Framework 4 POCO、存储库和规范模式的深入文章

http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/

【讨论】:

    【解决方案3】:

    我唯一不同的是在实现中,即在服务层中公开 IPostRepository 并在控制器中拥有一个 IPostService 类型的接口字段,就像另一层抽象一样,否则这是一个很好的例子 - 很好一,保罗。

    【讨论】:

    • 我在我的项目中也是如此。它是很好的抽象,还可以保持控制器动作简洁明了。
    • 嗨,Paul,已经有一段时间了,您在这里展示的内容非常有效。但是我根本没有进行任何单元测试:) 就在今天,我正在考虑进行单元测试,并使用 MOQ 框架来模拟数据,我该怎么做?我是否必须将存储库重新创建为模拟存储库,或者我可以重新使用我已经拥有的存储库?非常感谢。
    • @Saxman - 我会问一个新的问题。如何对我的 Repository 或其他东西进行单元测试。我可以在那里回答
    • :) 是的,Paul,我将如何测试我的存储库 :) 我们都必须处理的那些添加、删除、更新方法。更好的是如果我们可以模拟数据,或者不必连接到真实的数据库。谢谢。
    • 嗨,保罗,这是我的新问题 :) stackoverflow.com/questions/4738477/…
    猜你喜欢
    • 1970-01-01
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    • 2011-06-06
    • 2011-02-18
    相关资源
    最近更新 更多