C# 使用DbContext实现实体方式

C# 使用DbContext实现实体方式,c#,dependency-injection,inversion-of-control,dbcontext,solid-principles,C#,Dependency Injection,Inversion Of Control,Dbcontext,Solid Principles,通过直接依赖命令和查询处理程序中的DbContext,我了解到我违反了以下注释中的截止日期: DbContext是一个包,包含特定于请求的运行时数据和 将运行时数据注入构造函数会带来麻烦。让你的 直接依赖于DbContext的代码会导致代码 违反DIP和ISP,这使得维护变得困难 这是完全有道理的,但我不确定如何解决它可能使用IoC和DI 但最初是使用一个方法创建一个IUnitOfWork,该方法可用于查询上下文: public interface IUnitOfWork { IQuer

通过直接依赖命令和查询处理程序中的
DbContext
,我了解到我违反了以下注释中的截止日期:

DbContext
是一个包,包含特定于请求的运行时数据和 将运行时数据注入构造函数会带来麻烦。让你的 直接依赖于
DbContext
的代码会导致代码 违反DIP和ISP,这使得维护变得困难

这是完全有道理的,但我不确定如何解决它可能使用IoC和DI

但最初是使用一个方法创建一个
IUnitOfWork
,该方法可用于查询上下文:

public interface IUnitOfWork
{
    IQueryable<T> Set<T>() where T : Entity;
}

internal sealed class EntityFrameworkUnitOfWork : IUnitOfWork
{
    private readonly DbContext _context;

    public EntityFrameworkUnitOfWork(DbContext context)
    {
        _context = context;
    }

    public IQueryable<T> Set<T>() where T : Entity
    {
        return _context.Set<T>();
    }
} 
这也很好用

第一个问题是:我如何从命令处理程序修改上下文中的对象,因为我不能再直接依赖DbContext了?

比如:
context.Set().Add(实体)

据我所知,我必须为它创建另一个接口,以使用可靠的原则。例如,一个
ICommandEntities
,它将包含像
void Create(TEntity entity)
、更新、删除、回滚甚至重新加载这样的方法。然后在我的命令中依赖这个接口,但是我是否遗漏了一点,我们是否抽象得太深了

第二个问题是:在使用DbContext时,这是尊重可靠原则的唯一方法,还是违反这些原则“可以”的地方?


如果需要,我会使用Simple Injector作为IoC容器。

对于您的
EntityFrameworkUnitOfWork
,您仍然违反了以下部分:

DbContext是一个包,包含特定于请求的运行时数据,将运行时数据注入构造函数会带来麻烦

您的对象图应该是无状态的,并且状态应该在运行时通过对象图传递。您的
EntityFrameworkUnitOfWork
应如下所示:

internal sealed class EntityFrameworkUnitOfWork : IUnitOfWork
{
    private readonly Func<DbContext> contextProvider;

    public EntityFrameworkUnitOfWork(Func<DbContext> contextProvider)
    {
        this.contextProvider = contextProvider;
    }

    // etc
}
在我工作的系统中,我们几乎从不删除任何内容。因此,这阻止我们在该
i存储上使用
Delete
方法。当在该接口上同时使用
GetById
Create
时,更改已经很高,您将违反接口隔离原则,请非常小心不要添加更多方法。你甚至可能想把它们分开。如果你看到你的命令处理程序变大了,有很多依赖项,你可能想把它们分开,或者如果结果更差,你可以考虑从你的 IunITFWorks中返回存储库,但是你必须小心,不要丢失增加交叉切关切的可能性。 在使用DbContext时,这是唯一尊重可靠原则的方法吗

这绝对不是唯一的办法。我想说,最令人愉快的方法是应用领域驱动的设计并使用聚合根。在后台,您可能有一个O/RM,它为您保存一个完整的聚合,完全隐藏在命令处理程序和实体本身之外。如果您可以将这个对象图完全序列化为JSON并将其作为blob存储在数据库中,则更令人愉快。这从一开始就完全消除了使用O/RM工具的需要,但实际上这意味着您拥有了一个文档数据库。您最好使用一个真正的文档数据库,否则查询这些数据几乎是不可能的

或者这是一个违反原则“可以”的地方


无论你做什么,你都必须在某个地方违反这些坚实的原则。在哪里违反它们好,在哪里坚持它们好,这取决于您。

您真的需要将DbContext抽象为IUnitOfWork吗。即,您是否可能用另一个ORM替换EF?如果不是,为什么要引入看似不必要的复杂性呢?你肯定有一个观点——这种方法有优点也有缺点,我可以看到。我使用NHibernate,没有任何改变的计划。我直接使用NHibernate会话进行事务管理。不过,我倾向于使用存储库和查询模式,这样单元测试就更容易了。谢谢@Steven,我觉得你会来的。答案总是很好。感谢您通过注入
DbContext
指出该缺陷。我将在一个小的
I抽象中工作,通过一个
I工作单元
保存命令的装饰器,按读/写分离并保存更改。再次非常感谢!:-)
internal sealed class EntityFrameworkUnitOfWork : IUnitOfWork
{
    private readonly Func<DbContext> contextProvider;

    public EntityFrameworkUnitOfWork(Func<DbContext> contextProvider)
    {
        this.contextProvider = contextProvider;
    }

    // etc
}
interface IRepository<TEntity> {
    TEntity GetById(Guid id);
    // Creates an entity that gets saved when the transaction is committed,
    // optionally using an id supplied by the client.
    TEntity Create(Guid? id = null);
}