ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CodeGo.net>使用DbContext的SOLID方式

2019-11-19 22:07:12  阅读:301  来源: 互联网

标签:dependency-injection dbcontext solid-principles c inversion-of-control


通过直接依赖于命令和查询处理程序中的DbContext,我了解到我违反了StackOverflow user的注释中的SOLID-principles

The DbContext is a bag with request-specific runtime data and
injecting runtime data into constructors causes trouble. Letting your
code having a direct dependency of on DbContext causes your code to
violate DIP and ISP and this makes hard to maintain.

这完全有道理,但是我不确定如何使用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>();
    }
} 

现在,我可以在查询处理程序(接收数据)中依赖IUnitOfWork了,我已经解决了这一部分.

接下来,我需要查看命令(修改数据),我可以使用命令的装饰器解决对上下文所做的更改:

internal sealed class TransactionCommandHandler<TCommand> : IHandleCommand<TCommand> where TCommand : ICommand
{
    private readonly DbContext _context;
    private readonly Func<IHandleCommand<TCommand>> _handlerFactory;

    public TransactionCommandHandler(DbContext context, Func<IHandleCommand<TCommand>> handlerFactory)
    {
        _context = context;
        _handlerFactory = handlerFactory;
    }

    public void Handle(TCommand command)
    {
        _handlerFactory().Handle(command);
        _context.SaveChanges();
    }
}

这也很好.

第一个问题是:由于无法再直接依赖DbContext,如何从命令处理程序中修改上下文中的对象?

如:context.Set< TEntity>().Add(entity);

据我了解,我必须为此创建另一个接口才能使用SOLID原理.例如,ICommandEntities将包含诸如void Create< TEntity>(TEntity实体),更新,删除,回滚甚至重新加载之类的方法.然后依靠命令中的此接口,但是我在这里遗漏了一点吗?我们抽象得太深了吗?

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

如果需要,我将简单注入器用作IoC容器.

解决方法:

使用EntityFrameworkUnitOfWork,您仍然违反了以下部分:

The DbContext is a bag with request-specific runtime data and injecting runtime data into constructors causes trouble

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

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

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

    // etc
}

具有单个IQueryable< T>的抽象Set< T>()方法非常适合查询.使其向IQueryable< T>添加基于附加许可的过滤成为孩子.以后,无需更改查询处理程序中的任何代码行.

不过请注意,暴露IQueryable< T>的抽象(就像这个IUnitOfWork)抽象一样,它仍然违反SOLID原则.这是因为IQueryable< T>是一个泄漏性抽象,基本上意味着违反了依赖反转原则. IQueryable是一个泄漏抽象,因为在EF上运行的LINQ查询不会自动在NHibernate上运行,反之亦然.但至少在这种情况下,我们的SOLID稍微多了一点,因为在需要应用权限过滤或其他类型的过滤的情况下,它使我们不必通过查询处理程序进行大范围的更改.

试图从查询处理程序中完全抽象出O / RM是没有用的,只会导致您将LINQ查询移至另一层,或者使您恢复为SQL查询或存储过程.但是同样,在这里抽象O / RM也不是问题,而是能够在应用程序的正确位置应用跨领域关注点是问题.

最后,如果您迁移到NHibernate,则很可能必须重写一些查询处理程序.在这种情况下,您的集成测试将直接告诉您需要更改哪些处理程序.

但是关于查询处理程序已经足够了.让我们来谈谈命令处理程序.他们需要使用DbContext做更多的工作.如此之多,您最终可能会考虑让命令处理程序直接依赖于DbContext.但是我仍然希望他们不要这样做,而让我的命令处理程序仅依赖SOLID抽象.每个应用程序的外观可能会有所不同,但是由于命令处理程序通常是非常集中的,并且仅更改一些实体,因此我喜欢以下内容:

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

在我工作的系统中,我们几乎从未删除任何内容.因此,这阻止了我们对该IRepository< TEntity>使用Delete方法.当同时在该接口上同时具有GetById和Create时,更改已经很高,您将违反接口隔离原则,并且非常小心不要添加更多方法.您甚至可能想要拆分它们.如果您发现命令处理程序变得很大,并且具有许多依赖关系,则可能希望将它们拆分为Aggregate Services,或者如果结果更糟,则可以考虑从IUnitOfWork返回存储库,但是您必须注意不要丢失添加跨领域关注点的可能性.

Is this the only way respect the SOLID-principles when working with the DbContext

这绝对不是唯一的方法.我会说,最令人愉快的方法是应用域驱动设计并使用聚合根.在后台,您可能拥有一个O / RM,该O / RM可以为您持久保存完整的聚合,而对命令处理程序和实体本身则完全隐藏了.如果您可以将该对象图完全序列化为JSON并将其作为blob存储在数据库中,那就更令人高兴了.首先,这完全不需要使用O / RM工具,但这实际上意味着您拥有文档数据库.您最好使用真实的文档数据库,因为否则将几乎无法查询该数据.

or is this a place where its “okay” to violate the principles?

无论您做什么,都将不得不违反SOLID原则.最好由您来违反它们,以及在哪里坚持下去.

标签:dependency-injection,dbcontext,solid-principles,c,inversion-of-control
来源: https://codeday.me/bug/20191119/2039259.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有