autofacc:如何注入带有动态值的属性

本文关键字:动态 属性 注入 何注入 autofacc | 更新日期: 2023-09-27 18:10:10

通过Autofac,可以很容易地向给定程序集中的类实例注入CurrentDate属性的静态值:

builder.RegisterApiControllers(asm).WithProperty("CurrentDate", new DateTime(2012, 1, 13));

然而,如何注入动态值,例如由lamda () => { return DateTime.Now; }返回的值到CurrentDate属性?

autofacc:如何注入带有动态值的属性

听起来你可以使用非常标准的属性注入,像这样:

builder.RegisterApiControllers(asm)
    .OnActivating(e => { e.Instance.CurrentDate = DateTime.Now; });

注意,您可能需要强制转换e.Instance,因为它可能是Object类型。

更多信息请参阅文档中的生命周期事件。

再考虑一下,为什么不把初始化放在基类构造函数中呢?

public DateTime CurrentDate { get; private set; }
protected ApiController() { CurrentDate = DateTime.Now; }

当前日期并不是你需要DI容器提供的依赖项。

注册另一个提供动态值的服务(例如IDateTimeService)[我假设它真的比你想要的DateTime更复杂。]这个新服务的默认生存期是每个依赖的实例,但你也可以使用"每个匹配的生存期范围"。你的控制器已经在每个Http请求中被创建了。

现在只需从控制器中添加一个对IDateTimeService的依赖(在构造函数中)。在那个控制器的方法中,你现在可以从那个服务中获得你想要的动态值。

private static readonly IDateTimeService datetimeService;
public MyController (IDateTimeService datetimeService)
{
   this.datetimeService = datetimeService;
}

public void SomeMethod()
{
     var date = datetimeService.GetDate();
     ...
}

您需要这样编写自定义参数:

public class DelegateParameter : Parameter
{
    private readonly string _name;
    private readonly Func<object> _getValue;
    public DelegateParameter(string name, Func<object> getValue)
    {
        if (name == null) throw new ArgumentNullException("name");
        if (getValue == null) throw new ArgumentNullException("getValue");
        _name = name;
        _getValue = getValue;
    }
    public override bool CanSupplyValue(ParameterInfo pi, IComponentContext context, out Func<object> valueProvider)
    {
        PropertyInfo propertyInfo = GetProperty(pi);
        if (propertyInfo == null || propertyInfo.Name != _name)
        {
            valueProvider = null;
            return false;
        }
        valueProvider = _getValue;
        return true;
    }
    private static PropertyInfo GetProperty(ParameterInfo pi)
    {
        var methodInfo = pi.Member as MethodInfo;
        if (methodInfo != null && methodInfo.IsSpecialName && (methodInfo.Name.StartsWith("set_", StringComparison.Ordinal) && methodInfo.DeclaringType != null))
            return methodInfo.DeclaringType.GetProperty(methodInfo.Name.Substring(4));
        return null;
    }
}

然后使用它:

builder.RegisterApiControllers(asm).WithProperty(new DelegateParameter("CurrentDate", () => DateTime.Now));

如果您试图注入lambda表达式,而不是lambda表达式的结果,那么您有很多不完美的选择。这里只是几个例子;我相信还有更多。

Autofac

Google Project Hosting上的Autofac wiki记录了四种注入属性的方法。其中三个似乎使用常量或默认值——您提到了其中一个方法。

final似乎让开发人员对属性有了更多的控制。它使用OnActivating事件,在此期间您有几个选项。你可以:

  • 设置属性并希望它能坚持。
  • 如果属性缺乏可访问的setter,你可以使用反射来设置它,或者它的支持属性(默认情况下,m_PropertyName为一个名为PropertyName的属性,如果我没记错的话)。
  • 将实例封装在代理中,正如他们所说的:参见下面的多态性
多态性

ClassA包含要修改的属性Prop1。创建一个扩展ClassA的新类ClassB。如果Prop1virtual修饰符,您可以覆盖它。否则,使用new修饰符在ClassB中创建一个包含动态代码的类似属性。

在重写的情况下,您将需要实例化ClassB来代替ClassA。如果框架创建了自己的ClassA实例,这将不起作用,但是只要您创建了自己的ClassB实例并将它们传递给框架,您应该就可以了。

如果您正在使用一个新属性,除了实例化ClassB之外,您还必须确保无论何时访问新属性,对象都被强制转换为ClassB或后代类型。如果另一个框架被设计为使用ClassA,这通常不起作用,因为它将始终对ClassA类型进行操作,而不是ClassB类型,无论您的类型转换如何。

字节码操作

这是讨厌的东西,但它会做你想要的。c#通常编译成一种称为CIL的汇编/字节码语言。微软的变体是MSIL,但它与通用的CIL几乎相同。

我一直用Mono。Cecil for CLI/CLR. NET,单声道)字节码操作。它似乎完美地工作,一旦你掌握了它的窍门,它就会很好。然而,你必须知道两件事:

  1. 如何使用CIL
  2. 如何使用单声道塞西尔

第一个没那么糟。如果您有足够的CLI经验,那么您只需要几个带有详细表格的Wikipedia页面。如果你认为CLI就是"命令行界面"的意思,那么你可能会遇到困难。

Mono。另一方面,塞西尔在大约一年前(2012年)缺乏任何形式的适当文件。学习曲线异常陡峭。我花了几天时间想弄明白。当它工作的时候,它是惊人的。