我正在考虑尝试使用Munq来执行可选依赖项的属性注入。
如果没有在注入类中做这样的事情,这可能吗?:
MunqDependencyResolver.Container.Resolve<TTpe>();
此外,在这种情况下推荐使用属性注入(可选的依赖项)还是有更好的替代方案?
答案 0 :(得分:3)
从代码中调用容器通常是个坏主意。 Mark Seemann对此有一个good article。
物业注入本身很好,但通常只应在两种情况下使用:
在所有其他情况下,请进行构造函数注入。
使用Munq进行房产注入的方法如下:
container.Register<IDatabase>(c =>
new Database(c.Resolve<ILogger>())
{
// Property injection.
ErrorHandler = c.Resolve<IErorhandler>()
});
请注意,依赖项几乎不应该是可选的。可选的依赖项使应用程序代码更加复杂,因为这会强制代码区分两种类型的依赖项(实现和空值),并会导致代码中出现额外的if-null检查。大多数情况下,您可以简单地创建所需的依赖项,并添加注入/注册空实现(Null Object Pattern)。
答案 1 :(得分:0)
我想为遗留应用程序提升性能,所以我想出了这个,让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;
}