Dependency injection 将存储库接口作为参数传递给域类上的方法是否被认为是糟糕的设计?

Dependency injection 将存储库接口作为参数传递给域类上的方法是否被认为是糟糕的设计?,dependency-injection,entity-framework-4.1,domain-driven-design,repository,Dependency Injection,Entity Framework 4.1,Domain Driven Design,Repository,我们的领域模型目前非常贫乏。我们的实体大多是空壳,几乎完全是为了保存值和导航到集合而设计的 我们使用的是EF 4.1代码优先ORM,到目前为止,我们的设计一直是为了保护我们的新手开发人员在早期迭代期间查询上下文时免受可怕的“LINQ to Entities无法将Bla转换为存储表达式”异常的影响 我们在EF上有各种聚合根存储库接口。然而,impls中的一些代码块似乎应该由域负责。只要存储库接口在域中声明,并且impl在基础结构中(注入依赖项),将存储库接口作为参数传递给实体(或其他域)类上的方法

我们的领域模型目前非常贫乏。我们的实体大多是空壳,几乎完全是为了保存值和导航到集合而设计的

我们使用的是EF 4.1代码优先ORM,到目前为止,我们的设计一直是为了保护我们的新手开发人员在早期迭代期间查询上下文时免受可怕的“LINQ to Entities无法将Bla转换为存储表达式”异常的影响

我们在EF上有各种聚合根存储库接口。然而,impls中的一些代码块似乎应该由域负责。只要存储库接口在域中声明,并且impl在基础结构中(注入依赖项),将存储库接口作为参数传递给实体(或其他域)类上的方法是否被认为是错误的设计

例如,这会是坏的吗

public class EntityAbc {
    public void SaveTo(IEntityAbcRepository repos) {...}
    public void DeleteFrom(IEntityAbcRepository repos) {...}
}
如果某个特定实体需要访问其他聚合根存储库,该怎么办?这行不行,为什么

public void Save() {
    var abcRepos = DependencyInjector.Current.GetService<IEntityAbcRepository>();
    var xyzRepos = DependencyInjector.Current.GetService<IEntityXyzRepository>();
    // work with repositories
}
public void Save(){
var abcRepos=DependencyInjector.Current.GetService();
var xyzRepos=DependencyInjector.Current.GetService();
//使用存储库
}
更新1

我没有提到移动代码到应用层,因为我认为使用iTyTyBabCyPosik的一些代码涉及业务规则执行。存储库impl应该尽可能简单,对吗?它的主要职责应该只是对ORM进行简单的抽象,允许您查找/添加/更新/删除实体。错了吗

同样,这个问题也适用于其他非实体域类上的方法——工厂、服务,以及任何合适的模式。重点是,我要问的问题是关于域类上的任何方法,而不仅仅是实体类@Eranga,这是一个可以使用构造函数注入的地方,因为工厂和服务不是ORM的一部分

然后,应用层可以通过将存储库impl注入其构造函数并将其作为参数传递给域服务或工厂来协调流程。这是坏习惯吗

更新2

在这里添加另一个澄清。如果域只需要访问IEntityAbcRepository才能执行其Find()方法,该怎么办?在上面的示例中,SaveTo和DeleteFrom方法不会调用存储库接口上的任何add/update/delete方法

到目前为止,我们已经将find/add/update/delete方法组合在一个聚合根存储库界面上,以简化操作。但我认为没有什么能阻止我们将它们分成两个接口,就像这样:


  • IEntityAbcReadRepository与使用“服务定位器”模式的第二种方法相比,第一种方法更好。依赖性在第一种方法中更为明显

    这里有一些链接解释了为什么“服务定位器”是一个错误的选择

    这两种解决方案都源于EF不允许您使用构造函数注入这一事实。但是,您可以使用如上所述的属性注入。但这并不保证存在强制性依赖关系


    因此,您的第一种方法是更好的解决方案。

    简短回答:是

    长答案

    考虑在应用程序服务层中创建AbcService。此服务层位于域和基础架构之间。您可以将任意多个存储库注入AbcService。然后让服务处理SaveTo和DeleteFrom


    SaveTo和DeleteFrom,除非您要保存到另一个实体并从中删除,即不涉及数据访问,否则这些方法听起来不应该在域实体上,依我看。

    在域实体中具有持久性逻辑首先是依我看不好的设计。关注点的良好分离应该意味着域/业务逻辑与持久性逻辑分离,因此您的域类应该是独立的

    以前的实体框架版本可能不允许这样的分离,但我认为最新的版本解决了这个问题。不过我对EF不是很熟悉,所以我可能错了

    话虽如此,您可以将Save()和Delete()等方法放在哪里

    • 如果要将实体添加到存储库或从存储库中删除实体,repository.add()和repository.remove()是不错的选择。存储库基本上是一个实体的内存集合的假象,所以它的行为就像一个集合或一个具有适当方法的列表一样是有意义的

    • 如果要持久化对现有实体所做的更改,还有其他方法。您可以使用Repository.Save()方法,但这是一个糟糕的做法。通常,更改是在类似事务的上下文(如工作单元)中处理的更高级别操作的一部分,在这种情况下,您可以让操作在完成时保留其范围内的所有对象。例如,如果您对web应用程序使用一种方法,那么当请求结束时,更改会自动持久化。 或者,您可以依赖于对特定实体的ORM Save()方法的特别调用,希望该方法不应该移植到实体代码本身(例如,对于NHibernate,它在运行时可用于代理实体)

    [更新]

    将这一点与你随后的问题联系起来(尽管我不确定我是否完全理解):

    • 我认为将存储库拆分为ReadRepository和WriteRepository没有任何价值。在DDD中,存储库的责任显然是提供一个要从中查询的集合以及添加或删除的集合。这样仍然很有凝聚力

    • 摆弄自己的持久性不是实体的责任,所以它应该这样做