【问题标题】:Desing patterns with using EntityFramework?使用实体框架的设计模式?
【发布时间】:2013-01-29 09:11:27
【问题描述】:

界面

public interface IDinnerRepository 
{
    IQueryable<Dinner> FindAllDinners();
    IQueryable<Dinner> FindDinnersByText(string q);

    Dinner GetDinner(int id);

    void Add(Dinner dinner);
    void Delete(Dinner dinner);

    void Save();
}

继承自上述接口的类

public class DinnerRepository : NerdDinner.Models.IDinnerRepository
{
    NerdDinnerEntities db = new NerdDinnerEntities();

    Public IQueryable<Dinner> FindDinnersByText(string q)
    {
         return db.Dinners.Where(d => d.Title.Contains(q)
             || d.Description.Contains(q)
             || d.HostedBy.Contains(q));
    }

    public IQueryable<Dinner> FindAllDinners()
    {
         return db.Dinners;
    }

    public Dinner GetDinner(int id)
    {
        return db.Dinners.SingleOrDefault(d => d.DinnerID == id);
    }

    public void Add(Dinner dinner)
    {
        db.Dinners.AddObject(dinner);
    }

    public void Delete(Dinner dinner)
    {
        foreach (RSVP rsvp in dinner.RSVPs.ToList())
            db.RSVPs.DeleteObject(rsvp);

        db.Dinners.DeleteObject(dinner);
   }

   public void Save()
   {
        db.SaveChanges();
   }
}

在程序中的使用

public class DinnerOperation
{
    DinnerRepository dr = new DinnerRepository();

    // insert
    public void InsertDinner()
    {
        Dinner dinner = dr.GetDinner(5);
        dr.Dinner.Add(dinner);
        dr.Save();
    }

    // delete
    public void DeleteDinner()
    {
        Dinner dinner = dr.GetDinner(5);
        dr.Dinner.Delete(dinner);
        dr.Save();
    }
}

并且不使用存储库设计模式...

public class DinnerOperation
{
    DinnerEntities entity = new DinnerEntities();

    // insert
    public void InsertDinner()
    {
        Dinner dinner = entity.Dinners.Find(5);
        entity.Dinner.Add(dinner);
        entity.SaveChanges();
    }

    // delete
    public void DeleteDinner()
    {
        Dinner dinner = entity.Dinners.Find(5);
        entity.Dinner.Remove(dinner);
        entity.SaveChanges();
    }
}

问题

我不明白,在这里,我们为什么要使用设计模式? 当以这种方式将存储库设计模式与实体框架一起使用时,没有任何意义。

如何在实体框架中使用设计模式?什么时候将设计模式与实体框架结合使用才有意义?

【问题讨论】:

    标签: c# entity-framework design-patterns


    【解决方案1】:

    你几乎明白了。首先,重构您的晚餐操作类,以便可以将存储库实现注入其中。

    public class DinnerOperation
    {
      private IDinnerRepository dr;
      public DinnerOperation( IDinnerRespository repository ) {
         this.dr = repository;
      }
    
      // insert
      public void InsertDinner()
      {
          Dinner dinner = dr.GetDinner(5);
          dr.Dinner.Add(dinner);
          dr.Save();
      }
    
      // delete
      public void DeleteDinner()
      {
          Dinner dinner = dr.GetDinner(5);
          dr.Dinner.Delete(dinner);
          dr.Save();
      }
    }
    

    然后实现不同的仓库:

    public class EntityFrameworkDinnerRepository : IDinnerRepository
    public class NHibernateDinnerRepository : IDinnerRepository
    public class Linq2SqlDinnerRepository : IDinnerRepository
    public class MockDinnerRepository : IDinnerRepository
    ....
    

    然后使用您想要的任何存储库:

    var repository = new ....Repository();
    var operation = new DinnerOperation( repository );
    
    operation.GetDinner(5);
    

    存储库用于抽象您的具体数据提供者,从而使您的架构更加脆弱。

    想要切换到 nHibernate?

    如果你到处都有 EntityFramework,那就太痛苦了。 很简单,如果您使用存储库,您只需注入另一个存储库,您的业务逻辑不必更改。

    想要对您的业务逻辑进行单元测试?

    如果您坚持使用具体的数据提供者,那就太痛苦了。 很简单,如果你有存储库,你只需注入一个甚至不使用数据库的内存存储库。

    现在知道了吗?

    【讨论】:

    • 非常感谢。有很多东西要学:)。有没有关于这个主题的教程、文章等。我几乎明白了。您有什么建议可以更好地理解?
    • 我不记得任何简洁但完整的来源。只需在“存储库模式解释”上谷歌,阅读更多教程并研究与存储库模式配合得很好的依赖注入模式。
    • 从 EF 切换到 NH 或内存中的提供程序,或者使用暴露 IQueryable&lt;T&gt; 的存储库并不“容易”。
    • @Slauma:这非常简单,因为类库包含 IEnumerables 上的 AsQueryable 扩展方法。 msdn.microsoft.com/pl-pl/library/bb353734.aspx NH 也支持 linq。我为 linq2sql、nhibernate、ef、xpo 和 in-memory 实现了我的存储库,并且都实现了相同的接口。
    • 是的,NH 支持 LINQ,但与 EF(LINQ-to-Entities)不同的 LINQ 和 LINQ-to-Entities 既不是 LINQ-to-Objects 也不是 LINQ-to-SQL。您可以拥有相同的 LINQ 代码,全部编译,但一个可以工作,另一个不能工作,或者它的行为不同。它依赖于提供者,如果使用提供者 2 的代码正常工作,则无法使用使用提供者 1 的代码进行测试。在这里更好地解释我的意思:stackoverflow.com/a/6904479/270591(包括答案​​末尾的链接)。
    【解决方案2】:

    我在使用 ORM 时拒绝使用存储库模式。我认为在这种情况下它几乎没有任何好处。 (是的,我预计会被狂热者投下大约 2000 次反对票)。

    使用 EF,我为我的 Context 创建了一个接口

    public interface IDinnerContext : IDisposable
    {
        IDbSet<Dinner> Dinners;
    
        int SaveChanges();
    }
    

    然后我将此接口粘贴到我的 EF DatabaseContext 实现中,中提琴!我可以在任何地方使用 EF,使用 MOCK 实现对我的数据库访问进行单元测试,如果我愿意,可以使用注入,而不是最终得到 600 万个 GetByXXX 方法。 IQeryable 处理这个,我得到延迟执行。我真的不需要插入/删除方法,因为 IDBSet 已经有添加/删除。我的代码更简洁/更易于阅读,抽象更少。

    如果我确实遇到了在许多不同地方使用相同查询的情况,并且我确实希望这种情况很常见,那么我可以添加一些机制来支持它。但是,95% 的情况下,查询特定于负责业务逻辑 X 的组件(简单 GetBy 语句之外的任何内容)。所以我不认为这是一个问题。

    不要误会我的意思,在 ORM 变得相当不错之前,我一直虔诚地使用存储库模式。一旦 ORM 达到一定的复杂程度,我觉得可能不再需要存储库模式并开始在没有它的情况下进行项目......并且从未回头。我认为其他所有人最终都会朝着这个方向前进,或者类似的东西,但是旧习惯需要一段时间才能消失。 (我知道一些开发人员仍然坚持使用 lock(this),因为它仍在某些 MS 示例中)。

    【讨论】:

    • +1。不同的视角。我有点困惑,在你的回答之后。我认为,在决定使用哪一个时,需要经验。这给了我继续编码的士气。非常感谢...
    • 嗯,我想我对人们理解/知道的内容做了一些假设。人们坚持使用存储库模式有两个主要原因。 1)单元测试,2)查询重用。单元测试现在很容易在 EF 中使用 IDbContext 进行。查询重用也可以处理,虽然我相信在组件驱动的开发中,组件之间的实际查询使用几乎没有重叠,所以你只需要在组件级别管理它,这很容易做到。 .但我仍然更喜欢利用 EF/IQueryable 的灵活性。
    【解决方案3】:

    这是 存储库模式,它本身对于实体框架来说是多余的,因为 DbContext 同时用作存储库和工作单元,但它不可模拟 - 没有 @ 987654322@。因此,您最终将 DbContext 放入一个精简的存储库包装器中,以便您以后可以轻松地测试组件。

    我认为值得一提的是,我从未在 NHibernate 中使用过 Repository 模式,因为会话和会话工厂是接口 - ISession 和 ISessionFactory 相应地。

    如果你在某个地方(IRepository)通过接口使用存储库并注入它,通过模拟/存根进行测试会容易得多:

    public class DinnerOperation
    {
        private readonly IDinnerRepository repository;
    
        public DinnerOperation(IDinnerRepository dinnerRepository)
        {
            repository = dinnerRepository;
        }
    }
    

    当然,您必须使用选择的 IoC 容器为您注入正确的实例(在本例中为 DinnerRepository),或者“手动”执行 DI。

    这样您就可以针对模拟或存根存储库测试DinnerOperation 类。当你实例化DbContext 时,你不能。

    【讨论】:

    • 实体框架是可模拟的......现在。您可能正在考虑以前的版本,这使得这样做非常痛苦。现在,正如您在我上面的帖子中看到的那样,您可以将接口 (IDbContext) 用于上下文而不是 DbContext,从而使您可以轻松地测试组件。
    • @Keith 是的,以前很难做到。我一定错过了IDbContext 界面,但是感谢您的提示,我将进一步探讨该主题。 :)
    • @Keith 我找不到任何IDbContext 接口在 EF 5.0+ 中随时可用的概念。 (虽然有IDbSet)我能找到的只是自定义实现的解决方案;如果您有什么可以将我指向提到的界面,我将不胜感激。当然,如果这是一个定制的解决方案,那么我可以自己做同样的事情;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-29
    • 2011-06-11
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    • 2015-01-10
    • 1970-01-01
    相关资源
    最近更新 更多