Autofac委托工厂可以将参数传递给嵌套对象吗?

时间:2014-12-16 04:40:07

标签: c# autofac

我想使用Autofac委托工厂来解析一些对象图,其中一些嵌套对象需要传递参数。例如。如果Autofac delegate factory example中的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参数。

我见过几个类似的问题&amp;解决方案,但没有什么特别整洁。 Autofac-passing-parameter-to-nested-types建议注册lambda以显式实现工厂并解析嵌套依赖项。我确信这样可行,但如果在更深层次需要参数或者存在更多依赖项时,它会变得非常混乱。

我正在使用的临时解决方案是对此进行了改进,解决了Shareholding OnPreparing中的IQuoteService,并转发了由Autofac生成的工厂创建的参数。

 builder.RegisterType<Shareholding>().OnPreparing(e =>
            {
                e.Parameters = e.Parameters.Union(new[]
                {
                    new TypedParameter(typeof (IQuoteService), e.Context.Resolve<IQuoteService>(e.Parameters))
                });
            });

这样可以正常工作,避免手动解析其他参数,但实际上我需要做两次才能将参数转发到第二个嵌套级别。

我已考虑过,但未按照can-components-be-temporarily-registered-in-an-autofac-container的建议尝试使用BeginLifetimeScope(Action<ContainerBuilder>)。我想我必须手动实现工厂,但我可以注册uri,以便它可以在任何嵌套级别工作。

我真正希望能够做的是附加到WebQuoteService OnPreparing并访问委托工厂的参数。像这样的东西可以用于反射,但这显然不是理想的。

builder.RegisterType<WebQuoteService>().OnPreparing(e =>
{
    var parameters = e.Context._context._activationStack.Last().Parameters;
    e.Parameters = e.Parameters.Concat(parameters);
});

任何人都可以建议一个更清晰的替代方法,将参数传递给嵌套在两层深度的对象吗?

3 个答案:

答案 0 :(得分:2)

很抱歉自我回答,但没有更好的建议我认为我应该记录我的最佳解决方案。

在OnPreparing中,您可以使用反射来访问Autofac激活堆栈以及传递给委托工厂的参数。然后可以将这些添加到正在解析的嵌套组件的参数中。这适用于任何级别的嵌套(只需要将OnPreparing添加到需要参数的组件中。)

如此注册:

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);
    }
}

答案 1 :(得分:0)

嘿,我已经回复了一篇非常类似的帖子here。我认为这肯定对你有用,它比你上面的解决方法更清晰。

答案 2 :(得分:0)

从版本 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;
}