使用DbContext和DbSet而不是实现存储库和工作单元

时间:2015-02-06 14:10:39

标签: asp.net-mvc entity-framework repository-pattern unit-of-work

我见过很多关于实现存储库和工作单元的文章。我还看过有关如何添加额外复杂性的文章,因为DbContext已经在使用存储库和工作单元模式。

我将重构一个几乎每个实体都有一个存储库的应用程序,并希望尽可能多地删除复杂性。

任何人都可以解释/提供文章/博客/等链接,解释如何使用DbContext而不是我自己的存储库吗?

1 个答案:

答案 0 :(得分:5)

Rob Conery是个聪明人,但我不得不在这个问题上与他不同意。他建议的命令/查询方法只是从动作中删除查询逻辑(这是一些东西,但并不多)。仍然没有真正的抽象。并且,基本控制器方法也不是很好。虽然数据访问方法(这里是一个ORM)被抽象到代码中的一个位置,使得将来更容易更改,但它无法抽象API来处理该数据层,因此它几乎变得毫无意义。它真正为您节省的唯一一件事就是必须将private readonly AppContext context = new AppContext();放在每个控制器的顶部。您也许可以将两者结合起来,但如果数据层发生变化,您仍然需要修改每个查询类。

我认为这里的主要问题是每个人都在努力实现不同的目标。 Rob的建议方法是为了保持DRY。就个人而言,我抽象数据层的目的是为了能够在以后轻松切换数据访问方法。也许那是因为我过去已经被烧掉了,选择了一些获取数据的方法,从长远来看,这些方法最终无法理解。但我们至少都同意,实现存储库的传统方式是一个坏主意。

事实上,这是一个没有真正答案的问题。在某种程度上,您必须做最适合您和您的应用的事情。我已经解决的方法有点类似于存储库模式,我使用泛型方法而不是泛型类。如下所示:

public class Repository : IRepository
{
    protected readonly DbContext context;

    public Repository(DbContext context)
    {
        this.context = context;
    }

    public IEnumerable<TEntity> GetAll<TEntity>()
    {
        var dbSet = context.Set<TEntity>;
        return dbSet.ToList();
    }

    ...
}

我的实际课程比这复杂得多,但这足以说明要点。首先,注入上下文。这是我强烈不同意Rob的一个领域。也许如果你在你的上下文中快速和松散地玩,你可能不知道“它来自哪里”,但我使用依赖注入容器,它根据我的上下文请求创建一个实例。换句话说,我知道完全它来自哪里。

其次,因为这是一个带有泛型方法的标准旧类,所以我不需要在我的控制器操作中新建它们。我也不必为每个实体定义单独的存储库类。我可以简单地将这一个依赖项注入我的控制器并滚动:

public class FooController : Controller
{
    private readonly IRepository repo;

    public FooController(IRepository repo)
    {
        this.repo = repo;
    }

    ...
}

然后,如果我想获取一些Foo,我就这样做:

repo.GetAll<Foo>();

或者我想要一些Barrepo.GetAll<Bar>()

然后,您可以通过通用约束开始做有趣的事情。假设我希望能够只提取“已发布”的项目。我需要的只是一个界面:

public interface IPublishable
{
    PublishStatus Status { get; }
    DateTime? PublishDate { get; }
    DateTime? ExpireDate { get; }
}

然后,我只是将我希望成为“可发布”的任何实体实现此接口,或者从实现它的抽象类继承。一旦完成设置,我现在可以在我的存储库中执行以下操作:

public IEnumerable<TEntity> GetAllPublished<TEntity>()
    where TEntity : IPublishable
{
    var dbSet = context.Set<TEntity>();
    return dbSet.Where(m =>
        m.Status == PublishStatus.Published &&
        m.PublishDate.HasValue && m.PublishDate.Value <= DateTime.Now &&
        (!m.ExpireDate.HasValue || m.ExpireDate.Value > DateTime.Now)
    ).ToList();
}

现在,我在一个存储库中有一个方法可以为实现IPublishable的任何实体提取已发布的项目。代码重复是最基本的,更重要的是,如果我需要使用不同的ORM甚至是Web API来切换数据访问层,我只需要更改这个存储库类。我的所有其余代码都很愉快,好像什么都没发生一样。