4

我不想注入行为依赖关系,而是注入数据依赖关系 - 数据来自数据库或其他服务调用。为了达到这个目的,我的第一步是做类型到委托的绑定而不是类型到类型的绑定。到目前为止,我认为大多数.net国际奥委会容器可以做到这一点。价值提供者的惰性依赖注入(Unity,结构图,Ninject ...)

但我期望许多数据注入会产生不需要的延迟,因为这意味着很多往返数据存储的往返行程。我宁愿所有要注入的数据对象都会在一个请求中获取。

我想知道是否有一个框架或库,为此,或示例代码。

我相信一个框架或库将通过不断发展我之前的提议,以便委托绑定...

  1. 每个地方的数据请求到线程本地存储队列满足我的需要。
  2. 仅返回类型为Lazy<T>。 IOC容器完成喷射全部Lazy<T>类型

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

关于这种方法的思考?任何图书馆都这样做?

回答

3

直接向需要它的消费者注入数据可能在某些情况下有效,但在大多数情况下,消费者通过查询此数据(使用提供的参数询问数据)确定他需要哪些数据。

对于那些可以直接注入数据的情况(您可以将它们视为无参数查询),直接注入数据会使您的DI配置非常复杂。以一个依赖于系统当前时间的消费者为例。您可以将DateTime注入消费者(或甚至是Lazy<DateTime>)。另一位消费者可能需要当前用户的出生日期,因此此消费者也取决于DateTime。但是现在你在系统中不明确,因为DateTime依赖有两个含义。 DI容器在处理这个问题上不好,为了解决这个问题,你必须明确地告诉容器什么DateTime对于每个需要它的消费者意味着什么。这导致难以容纳的脆性结构。

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

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

您正在讨论查询数据库和Web服务,并需要缓存返回的数据的方法。因此,您需要的是定义查询的抽象,并且可以以可插入的方式将缓存和其他cross-cutting concerns应用于其中,并且无需对代码进行任何更改。

做到这一点的一个有效方法是通过定义限定查询对象(查询的输入参数)+返回类型,和一个接口,定义用于处理该查询的逻辑接口:

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[]>界面,而您可以用decorators包装IQueryHandler<T>实施,而无需任何人知道。编写缓存导致胎面局部stogage一个装饰是一个没有脑子:

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