我想注入数据依赖项,而不是注入一个行为依赖项 - 数据来自数据库或其他服务调用。为实现这一目标,我的第一步是进行类型到委托的绑定,而不是类型到类型的绑定。到目前为止,我认为大多数.net IOC容器都能做到这一点。
但我希望许多数据注入会产生不必要的延迟,因为这意味着许多往返于数据存储的往返。我宁愿在一个请求中获取要注入的所有数据对象。
我想知道是否有这样的框架或库,或示例代码。
我假设一个框架或库可以通过改进我之前的提议来满足我的需要,以便委托绑定......
Lazy< T >
。在IOC容器完成注入所有Lazy< T >
类型之后,程序执行总是会执行Lazy< T >.Value
个委托之一。当发生这种情况时,整个数据请求队列将同时发送并同时填充到TLS内的哈希表或字典中。因此,第一次Lazy< T >.Value
命中会很慢,但是作为IOC容器注入树一部分的所有其他Lazy< T >
项将会快速执行。
对这种提议方法的想法?任何图书馆都做这样的事情吗?
答案 0 :(得分: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>
实施,而无需任何人知道。编写一个装饰器,将结果缓存在胎面局部的滑动中是一个明智的选择:
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。