使用Entity Framework设计模式?

时间:2013-01-29 09:11:27

标签: c# entity-framework design-patterns

接口

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

问题

我不明白,在这里,我们为什么要使用设计模式? 以这种方式使用具有实体框架的存储库设计模式并不意味着什么。

如何将设计模式与权利框架一起使用?何时将设计模式与实体框架结合使用是有意义的?

3 个答案:

答案 0 :(得分:6)

你几乎得到了它。首先,重构您的晚餐操作类,以便可以将注册表实现注入其中。

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

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

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

现在知道了吗?

答案 1 :(得分:5)

我在使用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达到一定程度的复杂程度,我觉得可能不再需要存储库模式,并且开始没有它的项目....并且永远不会回头看。我认为其他所有人最终都会朝这个方向发展,或者类似的东西,但旧的习惯会因此而死。 (我知道一些仍然坚持使用锁(这个)的开发人员,因为它仍然存在于某些MS样本中。)

答案 2 :(得分:2)

这是存储库模式,它本身对于实体框架来说是超级的,因为DbContext同时充当了存储库和工作单元,但它不可模仿 - 有没有IDbContext。因此,您最终将DbContext放入精简存储库包装器中,以便稍后可以轻松地测试组件。

我认为值得一提的是我从未在NHibernate中使用Repository模式,因为会话和会话工厂都是接口 - ISessionISessionFactory

如果您通过某个地方的接口(IRepository)使用存储库并将其注入,则通过模拟/存根进行测试会更容易:

public class DinnerOperation
{
    private readonly IDinnerRepository repository;

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

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

通过这种方式,您可以针对模拟或存根存储库测试DinnerOperation类。当您实例化DbContext时,您不能。