【问题标题】:Abstracting EntityFramework.dll reference using Repository/UoW/DI使用 Repository/UoW/DI 抽象 EntityFramework.dll 引用
【发布时间】:2014-12-01 19:03:39
【问题描述】:

我一直在研究使用 EF(5.0 Beta Code First)作为我的 ORM/DAL 的新项目的最佳实践。关键考虑因素是可测试性、松散耦合设计和跨存储库的工作单元/事务支持。

我了解在 EF DbContext 中是 UoW 而 DbSet 是存储库,在服务层中,您可以跨多个由 DbContexts SaveChanges() 方法协调的存储库进行更改。这是我的示例配置:

public interface IMyContext
{
    IDbSet<Foo> Foos{ get; }

    IDbSet<Bar> Bars { get; }

    int SaveChanges();

    void Dispose();
}

public class EFMyContext : DbContext, IMyContext
{
    private IDbSet<Foo> _foos;
    private IDbSet<Bar> _bars;

    public EFMyContext()
        : base("name=MyConnectionString")
    {
        Database.SetInitializer<EFMyContext>(null);
    }

    public IDbSet<Foo> Foos
    {
        get
        {
            if (_foos == null)
            {
                _foos = this.Set<Foo>();
            }

            return _foos;
        }
    }

    public IDbSet<Bar> Bars
    {
        get
        {
            if (_bars == null)
            {
                _bars = this.Set<Bar>();
            }

            return _bars;
        }
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new FooConfiguration());
        modelBuilder.Configurations.Add(new BarConfiguration());

        base.OnModelCreating(modelBuilder);
    }

到目前为止一切都很好。如果我想用这个就是这样的上层

public class MyService : IMyService
{
    private IMyContext _context;

    public MyService(IMyContext context)
    {
        _context = context;
    }

    public void DoSomethingWithMyContext()
    {
        // fancy implementation code
    }
}

在这里我遇到了问题,因为我直接依赖于 EntityFramework.dll,因为 MyContext 公开了 IDBSet 类型的属性。这似乎不对,与this 问题非常相似。

所以我的问题是,如何抽象出对 EF 的直接依赖?我曾考虑引入我自己的存储库和工作单元来传递实时上下文,但这将如何影响更改跟踪和所有其他简洁的 EF 功能?

我正在研究的另一件事是管理上下文生命周期的 IoC 机制。那时我可以取消 UoW 吗?

抱歉含糊不清,我正处于研究饱和点,在实施之前需要一个明确的起点。

【问题讨论】:

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


    【解决方案1】:

    问自己一个问题:我的架构应该解决什么问题?除非你能提供无懈可击的答案,否则为什么从你的服务程序集中引用 EntityFramework.dll 是错误的,你就是在浪费你的时间。

    引用EntityFramework.dll其实并没有错。为什么应该这样?您的服务不会公开与 EF 相关的任何内容,因此它是完全可模拟的。如果您还想对服务代码进行单元测试,则必须确保您的服务以抽象方式与 EF 一起运行。在这种情况下,存储库可能是可行的方法,但它将是重型专用存储库 - 通常不建议使用 a generic nonsense。存储库将隐藏与 EF 相关的所有内容,您的服务将不知道您的应用程序中存在任何类似 EF 的内容。它还回答了您关于 EF 功能的问题 - 如果您想让它可测试,您的服务必须完全独立于这些功能。例如,您不应该让您的服务代码依赖于 LINQ-to-Entities 或延迟加载。它只是导致troubles in testing

    即使您实现了存储库,如果它们与您的服务在同一个程序集中并且该程序集引用 EntityFramework.dll,那么仍然没有任何问题。

    【讨论】:

    • 谢谢。我想我担心关注点分离。我可以选择使用另一个框架来实现 AnotherContext : IMyContext,即使不使用 EF 本身,我仍然需要对 EntityFramework.dll 的引用。我最好在 IMyContext 中将 IDbSet 更改为 IQueryable 吗?您能否通过 web/wcf 推荐 IoC 策略?
    • 您在现有项目中多久切换一次框架?作为一名经理,我会认为在开发过程中切换框架是大型开发人员的失败。你应该在一开始就明智地选择你的框架,并充分利用它的力量。如果您将来想切换框架,它很可能会影响您的代码,因为新框架不必支持旧框架的所有功能,或者它可以使用不同的原则。通过存储库抽象框架是可能的,但工作量很大。
    • 都是真的,但是...我是开发人员而不是经理。我并不是说这听起来很粗鲁,而是我的工作是考虑这些事情并在设计时考虑到未来的证明。如果这需要预先付出太多努力,那很好,我们不会这样做,但绝对值得考虑。当你选择它时,你会永远坚持下去,这似乎有点失败。
    • 我的英语又出问题了 :( 我不是经理,这句话应该以 If I would be a manaber 开头。无论如何我不同意设计应该计算未来可能发生的一切。特别是不要更换技术。那只会导致设计过于复杂,最终它对您没有帮助,因为技术通常不会在项目生命周期的后期完全取代。同时技术在项目的整个生命周期都伴随着项目是很常见的。因此,选择正确的技术是您的主要目标。
    • 只是为了明确一点,这与您的目标无关,以实现良好的关注点分离。即使您不打算替换 EF,您仍然可以实现良好的关注点分离。如果您将 EF 代码隐藏到称为存储库的东西中,或者只是隐藏您的查询和持久性逻辑的某个中间类,这并不重要。不要让模式驱动你的设计,当你遇到问题时使用模式来解决它。
    【解决方案2】:

    服务层中,您实际上并不需要使用IQueryable&lt;T&gt;

    因为这样,您将与支持 LINQORM 紧密耦合。 所有 LINQ 查询都应封装在具体的 DAL 中。

    您可以在 Service Layer 中定义与 DAL 无关的存储库:

    public interface IRepository<T>
    {
        T GetById(int id);
        IEnumerable<T> GetAll();
    
        // Filter is some DTO that will be mapped to 
        // concrete LINQ query inside your DAL
        IEnumerable<T> GetFiltered(Filter<T> filter);
    
        void Add(T item);
        void Update(T item);
        void Remove(T item);
    }
    

    更新:如果您喜欢 LINQ,您可以为 ObjectContext 和 ObjectSet 定义自己的接口:

    public interface IObjectContext : IUnitOfWork
    {
        IObjectSet<T> Set<T>() where T : class;
    }
    
    public interface IObjectSet<T> : IObjectQuery<T>
        where T : class
    {
        void Remove(T entity);
        void Add(T entity);
    }
    
    public interface IObjectQuery<out T> : IQueryable<T>, IQueryableWrapper
         where T : class
    {
        IObjectQuery<T> Include(string path);
    }
    

    然后您可以为 EF 编写适配器。 您还应该将查询转换层添加到您的适配器,因为 EF 无法理解表达式树中的接口。 以下是 QueryTranslator 的示例:https://stackoverflow.com/a/1846030/1275399

    【讨论】:

    • 老实说,我对 Linq 的依赖感到满意,尽管我知道它会带来低于最佳消耗的可能性。我们在这里使用 WCF 数据服务,它公开了 IQueryable,它允许开发人员按照他们的意愿进行查询,以牺牲重用和对错误查询的安全性为代价,从而提供更大的灵活性。
    【解决方案3】:

    您很可能没有 IMyContext 上的 DbSet 属性,而仅在实现上拥有 dbset。 IMyContext 应该只用于了解保存上下文(并将其处理掉),仅此而已。 因此,在存储库中,您可以拥有一个知道如何与 sql 对话的策略,例如使用 ef 并让它处理其中的 dbsets 部分。仅当您确实计划退出 EF 并在将来用其他东西替换它时,您才需要这样做。

    您可以找到有关此here 方法的更多详细信息

    【讨论】:

      猜你喜欢
      • 2015-08-26
      • 2019-05-03
      • 2016-07-12
      • 1970-01-01
      • 1970-01-01
      • 2014-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多