c# - 值(value)提供者的惰性评估依赖注入(inject)(Unity,结构图,Ninject ...)

标签 c# dependency-injection inversion-of-control lazy-loading ioc-container

我不想注入(inject)行为依赖性,而是想注入(inject)数据依赖性——数据来自数据库或其他服务调用。为实现这一点,我的第一步是进行类型到委托(delegate)的绑定(bind),而不是类型到类型的绑定(bind)。到目前为止一切顺利,我认为大多数 .net IOC 容器都可以做到这一点。

但我预计许多数据注入(inject)会造成不必要的延迟,因为这意味着多次往返数据存储。我宁愿在单个请求中获取所有要注入(inject)的数据对象。

我想知道是否有适用于此的框架或库,或示例代码。

我假设一个框架或库可以通过改进我之前的提议来满足我的需求,这样委托(delegate)绑定(bind)...

  1. 每个人将一个数据请求放入线程本地存储队列。
  2. 只返回 Lazy< T > 类型.

在 IOC 容器完成注入(inject)所有 Lazy< T > 之后类型,程序执行总是会执行 Lazy< T >.Value 之一代表们。当发生这种情况时,整个数据请求队列将同时发送并同时填充到 TLS 内的哈希表或字典中。因此第一个Lazy< T >.Value命中会很慢,但所有其他Lazy< T >然后,作为 IOC 容器注入(inject)树一部分的项目将快速执行。

对这个提议的方法有什么想法?有图书馆做这样的事情吗?

最佳答案

将数据直接注入(inject)需要它的消费者可能在某些情况下可行,但在大多数情况下,是消费者通过查询此数据(使用提供的参数请求数据)来确定他需要什么数据。

对于少数可以直接注入(inject)数据的情况(可以看成是无参数查询),直接注入(inject)数据会使你的DI配置变得极其复杂。以依赖于系统当前时间的消费者为例。你可以注入(inject) DateTime进入消费者(甚至是 Lazy<DateTime> )。另一个消费者可能需要当前用户的出生日期,所以这个消费者也依赖于 DateTime。 .但是现在你在系统中有歧义,因为 DateTime依赖有两个含义。 DI 容器在处理这个问题时很糟糕,要解决这个问题,您必须明确告诉容器什么 DateTime意味着每个需要它的消费者。这会导致难以包含的脆弱配置。

对于后一种情况(无参数查询),解决方案是定义可以解析的明确接口(interface)。在上面的示例中,您可以定义一个 ITimeProvider界面和一个IUserContext界面。每个消费者都可以依赖正确的接口(interface)。

对于前一种情况(参数化查询),您不需要框架;你需要适当的设计。

您正在谈论查询数据库和 Web 服务,并且需要一种方法来缓存返回的数据。因此,您需要的是定义查询的抽象,并以可以应用缓存和其他方式进行定义 cross-cutting concerns以一种可插入的方式提供给他们,使您不必对代码进行任何更改。

一个有效的方法是定义一个接口(interface)来定义查询对象(查询的输入参数)+返回类型,以及一个接口(interface)来定义处理该查询的逻辑:

public interface IQuery<TResult>
{
}

public interface IQueryHandler<TQuery, TResult> 
    where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

通过这些抽象,您可以像这样定义一个查询对象:

public class FindUsersBySearchTextQuery : IQuery<User[]>
{
    public string SearchText { get; set; }
    public bool IncludeInactiveUsers { get; set; }
}

该对象定义了一个查询,通过搜索文本查找用户,并允许包含或排除不活跃的用户,查询结果是User的数组。对象。

执行这个查询的逻辑可以实现如下:

public class FindUsersBySearchTextQueryHandler
    : IQueryHandler<FindUsersBySearchTextQuery, User[]>
{
    private readonly NorthwindUnitOfWork db;

    public FindUsersBySearchTextQueryHandler(
        NorthwindUnitOfWork db)
    {
        this.db = db;
    }

    public User[] Handle(FindUsersBySearchTextQuery query)
    {
        return (
            from user in this.db.Users
            where user.Name.Contains(query.SearchText)
            where user.IsActive || query.IncludeInactiveUsers
            select user)
            .ToArray();
    }
}

为什么这能解决您遇到的问题?这解决了您的问题,因为消费者可以依赖 IQueryHandler<FindUsersBySearchTextQuery, User[]>接口(interface),同时你可以包装 IQueryHandler<T>decorators 实现,无人知晓。编写一个将结果缓存在本地存储中的装饰器是一件很容易的事:

public class TlsCachingQueryHandlerDecorator<TQuery, TResult>
    : IQueryHandler<TQuery, TResult>
    where TQuery : IQuery<TResult>
    where TResult : class
{
    [ThreadStatic]
    private static TResult cache;

    private readonly IQueryHandler<TQuery, TResult> decorated;

    public ValidationQueryHandlerDecorator(
        IQueryHandler<TQuery, TResult> decorated)
    {
        this.decorated = decorated;
    }

    public TResult Handle(TQuery query)
    {
        return cache ?? (cache = this.decorated.Handle(query));
    }
}

任何固体 DI 容器都允许您注册这些装饰器,并允许您有条件地注册装饰器(基于装饰类型的类型信息)。这允许您仅将此缓存装饰器放置在可以安全缓存的类型上(根据您的条件)。

您可以在本文中找到有关此模型的更多信息:Meanwhile… on the query side of my architecture .

关于c# - 值(value)提供者的惰性评估依赖注入(inject)(Unity,结构图,Ninject ...),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13131144/

相关文章:

c# - WPF 两个列表框之间的单选

angular - 注入(inject)器与 ViewContainerRef.injector 与 ViewContainerRef.parentInjector

java - 有什么方法可以知道在 Spring 中从父类使用什么 bean 吗?

c# - Ninject - 不同的解决方案配置

c# - ASP.NET MVC 5 - 从数据库中选择数据的 LINQ 查询

c# - 总结为双倍时从 ForEach 循环转换为 Parallel.ForEach 循环会减慢速度

java - @Component 和 @Named 都用于同一个 bean 类

java - 如何从现场注入(inject)?

c# - 在 Simple Injector 中使用运行时数据获取实例

c# - DocX c# 库在一行中合并表字段