【问题标题】:Can Autofac delegate factories pass parameters to nested objects?Autofac 委托工厂可以将参数传递给嵌套对象吗?
【发布时间】:2014-12-16 04:40:07
【问题描述】:

我想使用 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参数。

我见过一些类似的问题和解决方案,但没有什么特别整洁的。 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&lt;ContainerBuilder&gt;)。我想我必须手动实现工厂,但我可以注册 uri,这样它就可以在任何嵌套级别上工作。

我真正想做的是附加到 WebQuoteService OnPreparing 并访问委托工厂的参数。可以使这样的东西与反射一起使用,但这显然并不理想。

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

谁能建议一种更简洁的替代方法来将参数传递给嵌套两层的对象?

【问题讨论】:

  • 非常接近,虽然不完全相同,这个问题:stackoverflow.com/questions/26327177/…
  • 类似,但我觉得使用代表否定了 cmets 对糟糕设计的看法。我认为委托是创建子系统的接口。该子系统需要一个 url,但树顶部的对象(例如 ShareHolding)不需要。我可以创建一个工厂对象并显式解析需要参数的组件,但是当 Autofac 如此接近时,这似乎是一种耻辱。

标签: c# autofac


【解决方案1】:

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

【讨论】:

    【解决方案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);
        }
    }
    

    【讨论】:

    • 哇,这真是太棒了,应该是 Autofac 的一部分!
    猜你喜欢
    • 1970-01-01
    • 2011-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多