【问题标题】:How to decouple my repositories and unitofwork in order to make them testable如何解耦我的存储库和工作单元以使它们可测试
【发布时间】:2015-08-25 09:39:14
【问题描述】:

实际上我已经实现了我的存储库和 unitofwork this way 并且我还发现了 informative answerMr.qujck 关于我的问题,这在很大程度上帮助了我,但我唯一的问题是根据他的主题回答我不知道应该如何以及在哪里实施我的 CRUD 操作。我还有 this question 仍未得到答复。如果有人可以帮助我,我将不胜感激。

【问题讨论】:

  • 您链接到的问题中的IRepository<> 是存储库模式的一个非常糟糕的示例,我完全理解您的困惑:here 是一个更好的示例。在示例中,DbContext 被注入到所有存储库和 UnitOfWork 中,因此 UnitOfWork 不关心或不需要了解存储库。
  • 感谢您的关注 Mr.qujck 我阅读了整篇文章。这很好,但它对我提出了一些问题。我有点糊涂了! 1.是否应该避免使用通用存储库?当我们只想进行 crud 操作时使用它们是否合理?使用它们是一个品味问题,还是有充分的理由不使用或使用它们?似乎人们对使用或不使用它们有很多争论。
  • 2.你参考的文章让我对整个想法有了很好的理解,但似乎并不完整,而且文章中没有任何源代码来总结文章。如果您向我提供完全实现这些想法的参考或资源,我将不胜感激。
  • 有关一些有用信息,请参阅here。就我个人而言,由于试图调整应用程序性能的无数问题,我已经离开了 EntityFramework。我现在有 IUnitOfWorkIRepository<> 抽象包装 Dapper 但没有任何有用的文章我可以指出对不起。
  • 谢谢亲爱的qujck先生

标签: c# entity-framework dependency-injection repository unit-of-work


【解决方案1】:

EntityFramework 已经为您提供了有关 CRUD 操作的所有功能,一般来说,您不需要测试 EntityFramework,因为我们只是假设它可以工作。它在该网站上显示的方式是,您创建一个比简单的 CRUD 操作更高的抽象层,将其构建在 EF 之上,并将其命名为 Repository(数据访问层)。所以在这种情况下,您只需要确保您的 DAL(在这种情况下也称为存储库)是解耦的。存储库唯一耦合到的是 EntityFramework 的 DbContext,它并不能真正解耦,因此您真正需要测试和向上解耦的最低抽象级别是您的 Repository 类。

【讨论】:

  • 实际上我想基于 DDD 原则创建我的 MVC 应用程序,所以我需要在我的存储库中用我自己的 crud 操作包装 entityframework dbcontext 方法,以防止用户直接访问上述方法应用我的不变量。如果您在第二个链接中查看 Mr.qujck 的答案,唯一错过的是实施 crud 操作。我希望你明白我的意思。
【解决方案2】:

在该示例中使用工作单元模式的方式并不能真正代表 UoW 的行为方式。存储库应该了解 UoW,但 UoW 不应该对您的存储库有任何引用。 UoW 应该只知道您的上下文。在下面的示例中,您从 UoW 中删除了存储库依赖项。对于测试,如果你真的需要,你可以实现一个假的 DbContext,但是这可能是矫枉过正。

我的另一个建议是向您的存储库添加一个进一步的抽象并创建一个服务层。这将充当您的业务层,这些将被注入到您的 MVC 控制器中。这样,如果您使用 DTO 或视图模型,您可以在该层中映射实体和 DTO,因此应用程序永远不会真正暴露您的底层 DAL。

    public interface IUnitOfWork : IDisposable
    {
        /// <summary>
        /// Flushes content of unit of work to the underlying data storage. 
        /// Causes unsaved entities to be written to the data storage.
        /// </summary>
        void Flush();

        /// <summary>
        /// Begins the transaction.
        /// </summary>
        ITransaction BeginTransaction();

        /// <summary>
        /// Ends transaction.
        /// Note: suggested pattern to manage a transaction is via *using* construct.
        /// You should set input param to null after calling the method.
        /// </summary>
        /// <example>
        /// using ( var tnx = uow.BeginTransaction() ) { /* do some work */ }
        /// </example>
        /// See also <seealso cref="ITransaction"/> interface for more details.
        void EndTransaction(ITransaction transaction);

        /// <summary>
        /// Inserts entity to the storage.
        /// </summary>
        void Create<TEntity>(TEntity entity) where TEntity : class;

        /// <summary>
        /// Updates entity in the storage.
        /// </summary>
        void Update<TEntity>(TEntity entity) where TEntity : class;

        /// <summary>
        /// Updates entity in the storage.
        /// </summary>
        void Merge<TEntity>(TEntity original, TEntity current) where TEntity : class;


        /// <summary>
        /// Deletes entity in the storage.
        /// </summary>
        void Delete<TEntity>(TEntity entity) where TEntity : class;


    }

    public interface IRepository<TEntity> where TEntity: class
    {
        void Create(TEntity entity);
        TEntity GetById(object id);
        IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> criteria);
          IEnumerable<TEntity> GetAll();
        void Delete(object id);
        void Delete(TEntity entity);
        void Update(TEntity entity);
        void Merge(TEntity original, TEntity current);
        IUnitOfwork UnitOfWork { get; }
    }




    public class EntityFrameworkUnitOfWork: DbContext, IUnitOfwork
    {
        public virtual void Flush()
        {
            //The DbContext Save Changes method..
            SaveChanges();
        }    

        public virtual void Create<TEntity>(TEntity entity) where TEntity : class
        {
            base.Set<TEntity>().Add(entity);
        }

       ..other method implementations...
     }


 public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        private readonly IUnitOfWork _unitOfWork;

        public Repository(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        ...other method implementations...
    }

【讨论】:

  • 感谢您的回复。我认为我之前介绍的资源应该是了解 UOW 和 Repository 的一个很好的参考,但我真的不知道他们为什么会分享错误的信息。但是,关于您的两种方法,您能否为我提供全面的资源,以帮助我以正确的方式学习这些模式。
猜你喜欢
  • 1970-01-01
  • 2016-10-14
  • 2011-10-16
  • 1970-01-01
  • 1970-01-01
  • 2012-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多