简体   繁体   English

Simple Injector:将导航属性注入存储库

[英]Simple Injector: injecting navigation properties into repositories

Let's assume we have an entity with multiple many to many navigation fields of the same type: 假设我们有一个实体,其中有多个相同类型的多个导航字段:

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

Bear in mind it's purely a made up example and it can be anything with two properties of the same type. 请记住,这纯粹是一个虚构的示例,可以是具有两个相同类型的属性的任何示例。

I'm currently running base generic repositories classes for a single entities registered in this manner . 我目前正在为以这种方式注册的单个实体运行基本通用存储库类。 The definition looks like this: 定义如下所示:

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

DI picking up the right typeparam and injects it into my repos, works like a charm. DI选择正确的typeparam并将其注入到我的仓库中,就像一个魅力。 I'm very new to DI and read this article but still struggle to see why is it so bad if it's making my life easier and hides EF specific methods out of controller's sight. 我对DI很陌生,并且阅读了这篇文章,但是仍然很难理解为什么它如此糟糕,如果它使我的生活更加轻松,并且将EF特定方法隐藏在控制器的视线之外。

And then I have N-to-many base repository class that handles relations between two entities. 然后,我有N对多的基本存储库类来处理两个实体之间的关系。 Maybe it's an oversimplification but I'd like to think each many-to-many relationships has principal and dependent so I will be adding liked/shared Users to the Post and never vise versa. 也许这是一个过分的简化,但我想认为每个多对多关系都具有主体和从属关系,因此我将在帖子中添加喜欢/共享的用户,而从不反对。 In my current implementation base repo class has an abstract property 在我当前的实现中,基本repo类具有抽象属性

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

so I can't make base repo class non-abstract and currently have to create multiple implementation of this class for each of my relationships explicitly specifying dependent property. 因此,我不能使基回购类成为非抽象类,并且当前必须为我的每个关系(显式指定依赖属性)创建此类的多个实现。

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

One way to get rid of it is to search for a right TPrincipal's property with a TDependent type via reflection, but there going to be many of these in current example. 摆脱它的一种方法是通过反射来搜索具有TDependent类型的正确的TPrincipal属性,但是在当前示例中会有很多这样的属性。 I never thought of how I can even handle multiple repos implementation for the same Principal to Dependent type as I'm not sure how can I make DI inject the right implementation of each repo to each controller: one responsible for likes and one responsible for shares. 我从来没有想过我怎么能为同一个委托人到从属类型处理多个回购协议实现,因为我不确定如何让DI向每个控制器注入每个回购协议的正确实现:一个负责喜欢,一个负责共享。

So my intuition and intent is to parametrize the property selecion somehow. 因此,我的直觉和意图是通过某种方式对属性选择进行参数化。 Seems like I need to inject another parameter to my repo that gets the right property automagically, but I have no idea how I can achieve it. 似乎我需要在我的仓库中注入另一个参数,以自动获得正确的属性,但是我不知道如何实现。 Should I inject the property here? 我应该在这里注入财产吗? But how and where exactly should I pick the right value for it in a general manner? 但是,我应该如何以及在何处以一般方式为它选择正确的价值? Should it be another constructor parameter? 它应该是另一个构造函数参数吗?

I don't agree with the article's interpretation of "mediation" between the data layer and the application logic. 我不同意本文对数据层和应用程序逻辑之间的“中介”的解释。 Mediation is an act of facilitating negotiation between the layers not concealing/stone-walling them. 调解是一种促进各层之间进行协商的行为,而不是将其隐藏/固定。 A repository should serve to simplify the interaction between application logic and data. 存储库应用于简化应用程序逻辑和数据之间的交互。 If you have chosen an ORM such as Entity Framework to serve as your data access layer, then my recommendation would be to embrace everything that it offers to make your code: 如果您选择了诸如Entity Framework之类的ORM作为您的数据访问层,那么我的建议是将其提供的所有内容用于编写代码:

  • Simpler to write. 编写更简单。
  • Simpler to understand. 更容易理解。
  • Simpler to maintain. 易于维护。

Generic repositories IMO are an anti-pattern in the sense that they encourage developers to think of entities individually rather than collectively as a domain structure. 通用存储库IMO是一种反模式,从某种意义上说,它们鼓励开发人员将实体单独而不是集体地视为域结构。 If I have Orders, customers, and order lines, do I need repositories for each? 如果我有订单,客户和订单行,是否需要每个仓库? If I create an order I also need to create order lines, and associate the order to a customer. 如果创建订单,则还需要创建订单行,并将订单与客户相关联。 With generic repositories I might have an OrderRepository : Repository<Order> with a Get<T> base method, but then I need to also get a Customer, does that mean a reference to a CustomerRepository too? 使用通用存储库,我可能会有一个OrderRepository : Repository<Order>和一个Get<T>基方法,但是随后我还需要获取一个Customer,这是否也意味着对CustomerRepository的引用? What about creating OrderLines? 如何创建订单行? They have dependencies on products and other elements. 它们依赖于产品和其他元素。 If the repositories are to serve to isolate my application logic from the entities then would that mean using an OrderLineRepository, ProductRepository etc. All just to create an order. 如果存储库用于将我的应用程序逻辑与实体隔离,则意味着要使用OrderLineRepository,ProductRepository等。所有这些都只是为了创建订单。 To isolate application code from entities and the like, repositories should not return entities. 为了将应用程序代码与实体等隔离开来,存储库不应返回实体。 If they do you are still dependent on EF behind the scenes. 如果他们这样做,您仍然依赖于幕后EF。 Lazy Loading will fail if you're outside of a DbContext's scope. 如果您不在DbContext的范围内,则延迟加载将失败。 Returning DTOs was one common work-around for this, but this leads to huge inefficiencies and inflexibility when serving up data while trying to hide Entity Framework. 返回DTO是解决此问题的一种常见方法,但这会在试图隐藏Entity Framework的同时提供数据时导致极大的效率低下和灵活性。 It also leads to brittle code or a lot of duplication as single-purpose repositories are shared between different areas of the application where each area constantly struggles with the need to get a bit more data than previous consumers, or get that data in a slightly different way. 由于单一用途存储库在应用程序的不同区域之间共享,因此还会导致易碎的代码或大量重复操作,其中每个区域不断地需要获取比以前的使用者更多的数据,或以稍微不同的方式获取数据方式。 You start to see a number of similar methods in the repository, or extra arguments in methods, or very ugly patterns like attempts to send in expression trees or "include" string lists into methods to attempt to abstract what EF can do for you. 您开始在存储库中看到许多类似的方法,或者方法中的其他自变量,或者非常丑陋的模式,例如尝试将表达式树或“包含”字符串列表发送到方法中以尝试抽象化EF可以为您做的事情。

Overall while these dependencies might look simple individually, the code needed to coordinate and test application logic becomes very bulky and complex. 总的来说,尽管这些依赖项看起来可能很简单,但是协调和测试应用程序逻辑所需的代码却变得非常庞大和复杂。

For me, repositories serve three key purposes: 对我来说,存储库具有三个主要目的:

  • To make application logic easier to test. 使应用程序逻辑更易于测试。 (Abstract the DB, not EF) (抽象数据库, 而不是 EF)
  • To serve as a factory to ensure that entities are initialized correctly. 用作工厂以确保正确初始化实体。
  • To centralize low-level filters and functions. 集中底层过滤器和功能。 (IsActive, authorization, multi-tenant, soft-delete, etc.) (IsActive,授权,多租户,软删除等)

You could argue that the "factory" responsibility should fall on a separate class which has dependencies on a repository or repositories. 您可能会争辩说,“工厂”责任应该属于一个单独的类,该类对一个或多个存储库具有依赖性。 From my perspective the repository has access to the information it needs from the DbContext so it keeps it simpler. 从我的角度来看,存储库可以从DbContext访问其所需的信息,从而使信息变得更简单。 That said, my repositories are not generic classes, but instead manage a vertical similar to how I structure controllers. 就是说,我的存储库不是泛型类,而是管理类似于我构造控制器的垂直类。 A repository serves the needs of a controller, being the needs of a key area of the application. 存储库满足控制器的需求,即应用程序关键区域的需求。 I may have additional repositories to serve other centralized concerns that are shared. 我可能还有其他存储库来服务于其他共享的集中关注点。 (Lookups, authentication, etc.) This helps ensure that repositories make my code easy to test, and easy to modify as the implementation of the repository serves one purpose. (查找,身份验证等)这有助于确保存储库使我的代码易于测试,并且易于实现,因为存储库的实现仅出于一个目的。 Changes to other areas of the application that may be interested in Orders don't impact my OrderManagementRepository which serves an OrderManagementController for example. 对应用程序的其他可能对订单感兴趣的区域的更改不会影响我的OrderManagementRepository,该订单管理例如为OrderManagementController服务。

The repository facilitates, or mediates interations with EF, not isolates. 该存储库促进或中介与EF的交互,而不是与EF分离。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用Simple Injector注入IUrlHelper - Injecting IUrlHelper with Simple Injector 使用Simple Injector在运行时注入数据解析的模型 - Injecting data resolved models at runtime with Simple Injector 使用简单注射器将 controller 注入 SignalR 集线器 - Injecting controller into SignalR Hubs using Simple Injector 简单注入器:在基础 class 中注入属性 - Simple Injector: Injecting a property in a base class 根据程序集,使用Simple Injector注入不同的依赖项 - Injecting different dependencies with Simple Injector depending on assembly 使用简单注入器将配置类注入构造函数 - Injecting a configuration class into a constructor using Simple Injector 使用 Simple Injector 自动注册通用存储库 - Auto-Register Generic Repositories with Simple Injector 使用ASP.NET Core 2.0将简单的Injector组件注入IHostedService - Injecting Simple Injector components into IHostedService with ASP.NET Core 2.0 将依赖项注入到OWIN中间件中,并使用Simple Injector根据每个Web请求 - Injecting a dependency into OWIN Middleware and per web-request with Simple Injector 使用ResolveUnregisteredType事件注入可能在Simple Injector中按请求更改的数据库配置值 - Injecting database configuration values that might change per request in Simple Injector using ResolveUnregisteredType event
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM