Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Entity framework 实体框架和DDD—在将实体传递到业务层之前加载所需的相关数据_Entity Framework_Domain Driven Design_Persistence_Ddd Repositories_Ddd Service - Fatal编程技术网

Entity framework 实体框架和DDD—在将实体传递到业务层之前加载所需的相关数据

Entity framework 实体框架和DDD—在将实体传递到业务层之前加载所需的相关数据,entity-framework,domain-driven-design,persistence,ddd-repositories,ddd-service,Entity Framework,Domain Driven Design,Persistence,Ddd Repositories,Ddd Service,假设您有一个域对象: class ArgumentEntity { public int Id { get; set; } public List<AnotherEntity> AnotherEntities { get; set; } } 它接收实体标识符,按id加载实体,并使用域服务在其上执行一些业务逻辑 问题: 这里的问题在于相关数据。ArgumentEntity有一个实体集合,只有当您通过Include/Load方法明确请求时,EF才会加载该集合。 Doma

假设您有一个域对象:

class ArgumentEntity
{
    public int Id { get; set; }
    public List<AnotherEntity> AnotherEntities { get; set; }
}
它接收实体标识符,按id加载实体,并使用域服务在其上执行一些业务逻辑

问题: 这里的问题在于相关数据。ArgumentEntity有一个实体集合,只有当您通过Include/Load方法明确请求时,EF才会加载该集合。 DomainService是业务层的一部分,不应了解持久性、相关数据和其他EF概念

DoDomething服务方法希望接收ArgumentEntity实例,该实例加载了另一个实体集合。 您可能会说-这很简单,只需在Repository.GetById中包含所需的数据,并用相关集合加载整个对象

现在让我们从简化的示例回到大型应用程序的实际情况:

  • ArgumentEntity要复杂得多。它包含多个相关集合,并且相关实体也有其相关数据

  • 您有多种DomainService方法。每个方法都需要加载不同的相关数据组合

  • 我可以想象出可能的解决方案,但所有这些都远远不够理想:

  • 始终加载整个实体->但这是低效的,而且通常是不可能的

  • 添加几个存储库方法:GetByIdOnlyHeader、getByIDWithAnotherities、GetByIdFullData以在controller->中加载特定的数据子集,但controller会知道要加载哪些数据并传递给每个服务方法

  • 添加几个存储库方法:GetByIdOnlyHeader、GetByIDWithAnotherities、GetByIdFullData以加载每个服务方法中的特定数据子集->效率低下,每个服务方法调用的sql查询。如果为一个控制器操作调用10个服务方法会怎么样

  • 每个域方法调用repository方法来加载额外的必需数据(例如:EnsureAnsureOtherEntitiesLoaded)->这很难看,因为我的业务逻辑意识到相关数据的EF概念

  • 问题: 在将实体传递到业务层之前,如何解决为实体加载所需相关数据的问题?

    好问题:)

    我认为“相关数据”本身并不是一个严格的EF概念。对于NHibernate、Dapper,甚至使用文件存储,相关数据都是一个有效的概念

    不过,我大部分同意其他观点。下面是我通常做的:我有一个存储库方法,在您的例子中是
    GetById
    ,它有两个参数:id和
    params表达式[]
    。然后,在存储库中,我执行include。通过这种方式,您的业务逻辑中不依赖于EF(如果需要,可以手动解析表达式以获得另一种类型的数据存储框架),并且每个BLL方法都可以自行决定实际需要哪些相关数据

    public async Task<ArgumentEntity> GetByIdAsync(int id, params Expression<Func<ArgumentEntity,object>>[] includes)
    {
        var baseQuery = ctx.ArgumentEntities; // ctx is a reference to your context
        foreach (var inlcude in inlcudes)
        {
           baseQuery = baseQuery.Include(include);
        }
        return await baseQuery.SingleAsync(a=>a.Id==id); 
    }
    
    public异步任务GetByIdAsync(int-id,params表达式[]包含)
    {
    var baseQuery=ctx.ArgumentEntities;//ctx是对上下文的引用
    foreach(变量inlcude in inlcudes)
    {
    baseQuery=baseQuery.Include(Include);
    }
    返回wait wait baseQuery.SingleAsync(a=>a.Id==Id);
    }
    
    在DDD的上下文中,您似乎遗漏了项目中导致出现此问题的一些建模方面。你所写的实体看起来没有很强的凝聚力。如果不同的流程(服务方法)需要不同的相关数据,那么您似乎还没有找到合适的聚合。考虑将实体分割成具有高内聚性的多个集合。然后,与特定聚合相关的所有进程都将需要此聚合包含的全部或大部分数据


    所以我不知道你的问题的答案,但是如果你能后退几步并重构你的模型,我相信你不会遇到这样的问题。

    在你的例子中,我可以看到方法
    DoSomethingWithArgumentEntity
    ,它显然属于应用层。此方法调用了属于数据访问层的
    存储库
    。我认为这种情况不符合经典的分层体系结构——您不应该直接从应用层调用DAL

    因此,您的代码可以用另一种方式重写:

    [HttpPost("{id}")]
    public IActionResult DoSomethingWithArgumentEntity(int id)
    {
        this.DomainService.DoDomething(id);
        ...
    }
    
    DomainService
    实现中,您可以从repo读取此特定操作所需的任何内容。这避免了您在应用层遇到的麻烦。在业务层,您将有更多的自由来实现读取:使用服务器存储库方法读取半满实体,或者使用EnsureXXX方法,或者其他方法。关于操作所需阅读内容的知识将被放入操作代码中,在应用层中您不再需要这些知识


    每次出现这样的情况,都是一个强烈的信号,表明你们的实体并不是预先设计好的。正如krzys所说,实体没有内聚部分。换句话说,如果您经常需要一个实体的各个部分,您应该拆分该实体。

    感谢您分享您的经验。这是否意味着您将标识符传递给BLL方法,并且每个BLL方法调用存储库来加载具有所需相关数据的实体?基本上是的。“传递id”可能意味着很多事情:有时它意味着BLL方法有一个实际的参数,有时它意味着BLL方法通过某种注入的服务访问id。但在一天结束时,每个BLL方法都会以某种方式知道id,然后他们自己决定需要加载哪些属性,并将相应的表达式传递给存储库方法。我想我应该澄清一下:这里的存储库是一个抽象,它属于IRepository类型,并通过DI注入。存储库接口是业务逻辑的一部分,而存储库实现是DAL的一部分。所以DAL不是从应用层调用的。问题1:传递标识符
    [HttpPost("{id}")]
    public IActionResult DoSomethingWithArgumentEntity(int id)
    {
        this.DomainService.DoDomething(id);
        ...
    }