如何在责任链中注入下一个处理程序的依赖关系? [英] How to inject the dependency of the next handler in a chain of responsibility?
问题描述
在我当前的项目中,我使用了很多责任链"模式.
In my current project, I'm using quite a few Chain of Responsibility patterns.
但是,我发现通过依赖项注入配置链有点尴尬.
However, I find it a bit awkward to configure the chain via dependency injection.
给出此模型:
public interface IChainOfResponsibility
{
IChainOfResponsibility Next { get; }
void Handle(Foo foo);
}
public class HandlerOne : IChainOfResponsibility
{
private DbContext _dbContext;
public HandlerOne(IChainOfResponsibility next, DbContext dbContext)
{
Next = next;
_dbContext = dbContext;
}
public IChainOfResponsibility Next { get; }
public void Handle(Foo foo) { /*...*/}
}
public class HandlerTwo : IChainOfResponsibility
{
private DbContext _dbContext;
public HandlerTwo(IChainOfResponsibility next, DbContext dbContext)
{
Next = next;
_dbContext = dbContext;
}
public IChainOfResponsibility Next { get; }
public void Handle(Foo foo) { /*...*/}
}
我的启动变为:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IChainOfResponsibility>(x =>
new HandlerOne(x.GetRequiredService<HandlerTwo>(), x.GetRequiredService<DbContext>())
);
services.AddTransient(x =>
new HandlerTwo(null, x.GetRequiredService<DbContext>())
);
}
如何更清晰地配置我的责任链?
How to configure my chain of responsibility more cleanly?
推荐答案
我找到了一个简单的解决方案,因为找不到任何可以满足我要求的东西.只要GetRequiredService
可以解析链中所有处理程序的所有构造函数依赖项,它似乎都可以正常工作.
I've hacked a simple solution, as I couldn't find anything that did what I wanted. Seems to be working fine, as long as the GetRequiredService
can resolve all constructor dependencies of all the handlers of the chain.
我的启动课程变为:
public void ConfigureServices(IServiceCollection services)
{
services.Chain<IChainOfResponsibility>()
.Add<HandlerOne>()
.Add<HandlerTwo>()
.Configure();
}
执行魔术的代码:
public static class ChainConfigurator
{
public static IChainConfigurator<T> Chain<T>(this IServiceCollection services) where T : class
{
return new ChainConfiguratorImpl<T>(services);
}
public interface IChainConfigurator<T>
{
IChainConfigurator<T> Add<TImplementation>() where TImplementation : T;
void Configure();
}
private class ChainConfiguratorImpl<T> : IChainConfigurator<T> where T : class
{
private readonly IServiceCollection _services;
private List<Type> _types;
private Type _interfaceType;
public ChainConfiguratorImpl(IServiceCollection services)
{
_services = services;
_types = new List<Type>();
_interfaceType = typeof(T);
}
public IChainConfigurator<T> Add<TImplementation>() where TImplementation : T
{
var type = typeof(TImplementation);
if (!_interfaceType.IsAssignableFrom(type))
throw new ArgumentException($"{type.Name} type is not an implementation of {_interfaceType.Name}", nameof(type));
_types.Add(type);
return this;
}
public void Configure()
{
if (_types.Count == 0)
throw new InvalidOperationException($"No implementation defined for {_interfaceType.Name}");
bool first = true;
foreach (var type in _types)
{
ConfigureType(type, first);
first = false;
}
}
private void ConfigureType(Type currentType, bool first)
{
var nextType = _types.SkipWhile(x => x != currentType).SkipWhile(x => x == currentType).FirstOrDefault();
var ctor = currentType.GetConstructors().OrderByDescending(x => x.GetParameters().Count()).First();
var parameter = Expression.Parameter(typeof(IServiceProvider), "x");
var ctorParameters = ctor.GetParameters().Select(x =>
{
if (_interfaceType.IsAssignableFrom(x.ParameterType))
{
if (nextType == null)
return Expression.Constant(null, _interfaceType);
else
return Expression.Call(typeof(ServiceProviderServiceExtensions), "GetRequiredService", new Type[] { nextType }, parameter);
}
return (Expression)Expression.Call(typeof(ServiceProviderServiceExtensions), "GetRequiredService", new Type[] { x.ParameterType }, parameter);
});
var body = Expression.New(ctor, ctorParameters.ToArray());
var resolveType= first ? _interfaceType : currentType;
var expressionType = Expression.GetFuncType(typeof(IServiceProvider), resolveType);
var expression = Expression.Lambda(expressionType, body, parameter);
var compiledExpr = (Func<IServiceProvider, object>)expression.Compile();
_services.AddTransient(resolveType, compiledExpr);
}
}
}
PS ::我正在回答自己的问题以供将来参考(我自己,并希望是其他人),但我希望对此有一些反馈.
PS.: I'm answering my own question for future reference (myself and hopefully others), but I'd love some feedback on this.
这篇关于如何在责任链中注入下一个处理程序的依赖关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!