简单的注入器:将导航属性注入存储库

时间:2018-07-11 19:36:26

标签: c# entity-framework simple-injector

假设我们有一个实体,其中有多个相同类型的多对多导航字段:

class Post : IdProvider<TPrimaryKey>
    where TKey : struct
{
    ...
    public virtual ICollection<User> LikedBy { get; set; }
    public virtual ICollection<User> SharedBy { get; set; }
}

请记住,这纯粹是一个示例,它可以是具有两个相同类型的属性的任何东西。

我目前正在为this manner中注册的单个实体运行基本通用存储库类。定义如下:

EntityRepository<TKey, T> : EntityRepositoryBase, IEntityRepository<TKey, T>
    where T : class, IIdProvider<TKey> 
    where TKey : struct 

DI选择正确的typeparam并将其注入到我的存储库中,就像一个魅力一样。我是DI的新手,读过this article,但仍然很难理解为什么它使我的生活变得更加轻松并且将EF特定方法隐藏在控制器的视线之外如此糟糕。

然后,我有N对多的基本存储库类来处理两个实体之间的关系。也许这是一个过分的简化,但我想认为每个多对多关系都具有主体和从属关系,因此我将在帖子中添加喜欢/共享的用户,而从不反对。在我当前的实现中,基本的repo类具有抽象属性

public abstract Expression<Func<TPrincipal, ICollection<TDependent>>> GetDependentCollectionExpression { get; }

所以我不能使基本repo类成为非抽象类,并且当前必须为我的每个关系(显式指定依赖属性)创建此类的多个实现。

class PostToLikedUsersRepository : PrincipalToManyDependentRepository<Post, User>
{
    ...
    public override Expression<Func<Post, ICollection<User>>> GetDependentCollectionExpression
    {
        get { return item => item.LikedBy; }
    }
}

摆脱它的一种方法是通过反射来搜索具有TDependent类型的正确的TPrincipal属性,但是在当前示例中会有很多这样的属性。我从来没有想过我怎么能为同一个委托人到从属类型处理多个回购协议实现,因为我不确定如何让DI向每个控制器注入每个回购协议的正确实现:一个负责喜欢,一个负责共享

因此,我的直觉和意图是通过某种方式对属性选择进行参数化。似乎我需要在我的仓库中注入另一个参数,以自动获得正确的属性,但是我不知道如何实现。我应该在这里注入财产吗?但是,我应该如何以及在何处以一般方式为它选择正确的价值?应该是另一个构造函数参数吗?

1 个答案:

答案 0 :(得分:1)

我不同意本文对数据层和应用程序逻辑之间的“中介”的解释。调解是促进 not 层之间的协商的行为,而不是隐藏/石化它们。存储库应用于简化应用程序逻辑和数据之间的交互。如果您选择了诸如Entity Framework之类的ORM作为数据访问层,那么我的建议是将其提供的所有内容用于编写代码:

  • 编写简单。
  • 简单易懂。
  • 易于维护。

通用存储库IMO是一种反模式,从某种意义上说,它们鼓励开发人员将实体单独而不是集体地视为域结构。如果我有订单,客户和订单行,是否需要每个仓库?如果创建订单,则还需要创建订单行,并将订单与客户相关联。使用通用存储库,我可能有一个使用OrderRepository : Repository<Order>基本方法的Get<T>,但是随后我还需要获得一个Customer,这是否也意味着对CustomerRepository的引用?如何创建订单行?它们依赖于产品和其他元素。如果存储库用于将我的应用程序逻辑与实体隔离,则意味着要使用OrderLineRepository,ProductRepository等。所有这些都只是为了创建订单。为了将应用程序代码与实体等隔离开来,存储库不应返回实体。如果他们这样做,那么您仍然仍然依赖于幕后的EF。如果您不在DbContext的范围内,则延迟加载将失败。返回DTO是解决此问题的一种常见方法,但这会在试图隐藏Entity Framework的同时提供数据时导致极大的效率低下和灵活性。由于单一用途存储库在应用程序的不同区域之间共享,因此还会导致易碎的代码或大量重复操作,其中每个区域不断地需要获取比以前的使用者更多的数据,或以稍微不同的方式获取数据办法。您开始在存储库中看到许多类似的方法,或者在方法中看到其他参数,或者看到非常丑陋的模式,例如尝试将表达式树或“包含”字符串列表发送到方法中以尝试抽象EF可以为您做的事情。 / p>

总体上,尽管这些依赖项看起来可能很简单,但是协调和测试应用程序逻辑所需的代码却变得非常庞大和复杂。

对我来说,存储库具有三个主要目的:

  • 使应用程序逻辑更易于测试。 (提取数据库,不是 EF)
  • 用作工厂以确保正确初始化实体。
  • 集中低级过滤器和功能。 (IsActive,授权,多租户,软删除等)

您可能会争辩说,“工厂”责任应该属于一个独立的类,该类对一个或多个存储库具有依赖性。从我的角度来看,存储库可以从DbContext访问其所需的信息,从而使信息变得更简单。就是说,我的存储库不是泛型类,而是管理类似于我构造控制器的垂直类。存储库满足控制器的需求,即应用程序关键区域的需求。我可能还有其他存储库来服务于其他共享的集中关注点。 (查找,身份验证等)这有助于确保存储库使我的代码易于测试,并且易于实现,因为存储库的实现仅出于一个目的。对应用程序的其他可能对订单感兴趣的区域的更改不会影响我的OrderManagementRepository,该订单管理例如为OrderManagementController服务。

该存储库可促进或中介与EF的交互,而不是与之隔离。