6

界面

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

问题

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

如何将设计模式与实体框架一起使用?什么时候将设计模式与实体框架一起使用才有意义?

4

3 回答 3

7

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

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。很简单,如果您使用存储库,您只需注入另一个存储库,您的业务逻辑不必更改。

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

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

现在明白了?

于 2013-01-29T09:21:52.620 回答
7

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

使用 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 样本中)。

于 2013-01-31T20:51:22.290 回答
2

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

我认为值得一提的是,我从未在 NHibernate 中使用过 Repository 模式,因为会话和会话工厂是接口——因此ISession也是如此ISessionFactory

如果您在某处(IRepository)通过接口使用存储库并将其注入,则通过模拟/存根进行测试会容易得多:

public class DinnerOperation
{
    private readonly IDinnerRepository repository;

    public DinnerOperation(IDinnerRepository dinnerRepository)
    {
        repository = dinnerRepository;
    }
}

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

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

于 2013-01-29T09:18:00.220 回答