Autofac 委托工厂是否可以将参数传递给嵌套对象
本文关键字:参数传递 嵌套 对象 工厂 是否 Autofac | 更新日期: 2023-09-27 18:34:27
来解决对象图,其中某些嵌套对象需要传递参数。 例如,如果 Autofac 委托工厂示例中的 QuoteService 需要一个 url 来检索数据,或者 QuoteService 本身有一个需要参数的依赖项。
public class WebQuoteService : IQuoteService
{
public WebQuoteService(Uri source)
{
}
}
public class Shareholding
{
public Shareholding(string symbol, uint holding, IQuoteService quoteService)
{
}
}
我希望能够像这样声明和注册一个委托:
public delegate Owned<Shareholding> ShareholdingFactory(string symbol, uint holding,
Uri source);
builder.RegisterGeneratedFactory<ShareholdingFactory>();
我遇到的问题是Autofac无法解析WebQuoteService的uri参数。
我见过一些类似的问题和解决方案,但没有特别整洁的。Autofac-passing-parameter-to-nested-types建议注册一个lambda来显式实现工厂并解析嵌套依赖关系。我相信这会起作用,但如果在更深层次上需要参数或有更多的依赖项时,它会变得非常混乱。
我正在使用的临时解决方案对此进行了改进,解决了持股OnPreparing
中的IQuoteService,并转发了Autofac生成的工厂创建的参数。
builder.RegisterType<Shareholding>().OnPreparing(e =>
{
e.Parameters = e.Parameters.Union(new[]
{
new TypedParameter(typeof (IQuoteService), e.Context.Resolve<IQuoteService>(e.Parameters))
});
});
这工作正常,避免手动解析其他参数,但我实际上需要这样做两次才能将参数转发到第二个嵌套级别。
我已经考虑过,但没有尝试使用BeginLifetimeScope(Action<ContainerBuilder>)
can-components-be-temporaryly-register-in-autofac-container的建议。我想我必须手动实现工厂,但我可以注册 uri,以便它可以在任何嵌套级别工作。
我实际上希望能够做的是附加到WebQuoteService OnPrepare并访问委托工厂的参数。像这样的东西可以与反射一起工作,但这显然不理想。
builder.RegisterType<WebQuoteService>().OnPreparing(e =>
{
var parameters = e.Context._context._activationStack.Last().Parameters;
e.Parameters = e.Parameters.Concat(parameters);
});
任何人都可以建议一个更干净的替代方案,将参数传递给嵌套两级深的对象?
很抱歉自我回答,但如果没有更好的建议,我认为我应该记录我拥有的最佳解决方案。
在 OnPrepreing 中,可以使用反射来访问 Autofac 激活堆栈和传递给委托工厂的参数。然后,可以将这些添加到要解析的嵌套组件的参数中。这适用于任何级别的嵌套(只需将其添加到需要参数的组件的 OnPrepare 中。
像这样注册:
builder.RegisterType<WebQuoteService>()
.OnPreparing(AutofacExtensions.ForwardFactoryParameters);
使用此帮助程序类:
public static class AutofacExtensions
{
private static readonly FieldInfo ContextFieldInfo;
private static readonly FieldInfo ActivationStackFieldInfo;
static AutofacExtensions()
{
var autofacAssembly = typeof(IInstanceLookup).Assembly;
Type instanceLookupType = autofacAssembly.GetType("Autofac.Core.Resolving.InstanceLookup");
ContextFieldInfo = instanceLookupType.GetField("_context", BindingFlags.Instance | BindingFlags.NonPublic);
Type resolveOperationType = autofacAssembly.GetType("Autofac.Core.Resolving.ResolveOperation");
ActivationStackFieldInfo = resolveOperationType.GetField("_activationStack", BindingFlags.Instance | BindingFlags.NonPublic);
}
public static IResolveOperation Context(this IInstanceLookup instanceLookup)
{
return (IResolveOperation)ContextFieldInfo.GetValue(instanceLookup);
}
public static IEnumerable<IInstanceLookup> ActivationStack(this IResolveOperation resolveOperation)
{
return (IEnumerable<IInstanceLookup>)ActivationStackFieldInfo.GetValue(resolveOperation);
}
/// <summary>
/// Pass parameters from the top level resolve operation (typically a delegate factory call)
/// to a nested component activation.
/// </summary>
public static void ForwardFactoryParameters(PreparingEventArgs e)
{
var delegateFactoryActivation = ((IInstanceLookup) e.Context).Context().ActivationStack().Last();
e.Parameters = e.Parameters.Concat(delegateFactoryActivation.Parameters);
}
}
从版本 6 开始,Matt 的答案不再起作用。下面是正确的。
public static void ForwardFactoryParameters(PreparingEventArgs e)
{
var ctx = e.Context;
var oper = ctx.GetType().GetProperty("Operation").GetValue(ctx);
var requestStack = oper.GetType().GetProperty("InProgressRequests").GetValue(oper) as SegmentedStack<ResolveRequestContext>;
if (requestStack.Count == 1)
{
//Nothing to do; we are on the first level of the call stack.
return;
}
var entryRequest = requestStack.Last();
e.Parameters = entryRequest.Parameters;
}