可选依赖项的 Munq 属性注入

本文关键字:Munq 属性 注入 依赖 | 更新日期: 2023-09-27 18:31:45

我正在考虑尝试使用 Munq 对可选依赖项进行属性注入。

如果不在注入的类中执行类似操作,这可能吗?

MunqDependencyResolver.Container.Resolve<TTpe>();

此外,在这种情况下是否建议使用属性注入(可选依赖项)还是有更好的替代方案?

可选依赖项的 Munq 属性注入

从代码中调用容器通常是一个坏主意。马克·西曼(Mark Seemann)有一篇关于此的好文章。

属性注入本身是可以的,但通常只应在两种情况下使用:

    依赖项
  1. 确实是可选的,应用程序在缺少依赖项时可以正常工作。或
  2. 构造函数注入是不可能的,例如由于循环依赖关系。

在所有其他情况下,进行构造函数注入。

使用 Munq 进行属性注入的方法如下:

container.Register<IDatabase>(c =>
    new Database(c.Resolve<ILogger>())
    {
        // Property injection.
        ErrorHandler = c.Resolve<IErorhandler>()
    });

请注意,依赖项几乎永远不应该是可选的。可选依赖项使应用程序代码更加复杂,因为这会强制代码区分两种类型的依赖项(实现和 null 值),并将导致代码中额外的 if-null 检查。大多数情况下,您可以简单地使依赖项成为必需的,并添加注入/注册一个空实现(空对象模式)。

我想提高

旧版应用程序的性能,所以我想出了这个,让 Munq 使用以下 DepencyAttribute

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class DependencyAttribute : Attribute
{
    // Fields
    private readonly string name;
    /// <summary>
    /// Create an instance of DependencyAttribute with no name
    /// </summary>
    public DependencyAttribute()
        : this(null)
    {
    }
    /// <summary>
    /// Create an instance of DependencyAttribute with name
    /// </summary>
    /// <param name="name"></param>
    public DependencyAttribute(string name)
    {
        this.name = name;
    }
    /// <summary>
    /// The name specified in the constructor
    /// </summary>
    public string Name
    {
        get
        {
            return name;
        }
    }
}

    public static IRegistration LegacyRegister(this IocContainer container, Type from, Type to, string name = null)
    {
        if (from.ContainsGenericParameters)
            container.Register(name, from, to);
        var reg = container.Register(name, from, Create(to));
        return reg;
    }
    public static IRegistration LegacyRegister<TFrom,TTo>(this IocContainer container, string name = null)
    {
        return container.LegacyRegister(typeof (TFrom), typeof (TTo), name);
    }
    public static System.Func<IDependencyResolver, object> Create(Type from)
    {
        var container = Expression.Parameter(typeof(IDependencyResolver), "container");
        var ctor = BuildExpression(from, container);
        var block = InitializeProperties(ctor, container);
        var func = Expression.Lambda<System.Func<IDependencyResolver, object>>(block, new[] { container});
        return func.Compile();
    }
    private static Expression InitializeProperties(NewExpression ctor, ParameterExpression container)
    {
        var expressionList = new List<Expression>();
        var instance = Expression.Variable(ctor.Type, "ret");
        var affect = Expression.Assign(instance, ctor);
        //expressionList.Add(instance);
        expressionList.Add(affect);
        var props = from p in instance.Type.GetProperties(BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Public)
            let da = p.GetCustomAttributes(typeof (DependencyAttribute), true).Cast<DependencyAttribute>().FirstOrDefault()
            where da != null
            select new {Property = p, da.Name};
        var propsSetters = from p in props
            let resolve = p.Name == null ?
                Expression.Call(container, "Resolve", new[] {p.Property.PropertyType})
                : Expression.Call(container, "Resolve", new[] {p.Property.PropertyType}, Expression.Constant(p.Name, typeof (string)))
            select Expression.Call(instance, p.Property.GetSetMethod(true), resolve);
        expressionList.AddRange(propsSetters.ToList());
        expressionList.Add(instance);
        var block = Expression.Block(ctor.Type, new[] { instance }, expressionList);
        return block;
    }
    private static NewExpression BuildExpression(Type type, ParameterExpression container)
    {
        ConstructorInfo constructorInfo = GetConstructorInfo(type);
        var parameters = from p in constructorInfo.GetParameters()
                    let da = p.GetCustomAttributes(typeof(DependencyAttribute), true).Cast<DependencyAttribute>().FirstOrDefault()
                        ?? new DependencyAttribute()
                    select new { Param = p, da.Name };

        var list = parameters.Select(p => 
            Expression.Call(container, "Resolve", new [] { p.Param.ParameterType },
                p.Name == null ? new Expression[0] : new Expression[] { Expression.Constant(p.Name, typeof(string)) }));
        return Expression.New(constructorInfo, list);
    }
    private static ConstructorInfo GetConstructorInfo(Type implType)
    {
        ConstructorInfo constructorInfo = implType.GetConstructors().OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
        if (constructorInfo == null)
            throw new ArgumentException(string.Format("The requested class {0} does not have a public constructor.", (object)implType));
        return constructorInfo;
    }