2012-04-20 46 views
1

我考虑尝试使用Munq做可选依赖的财产注射。Munq财产注射可选依赖

这是可能的,而不做在注入的类?:

MunqDependencyResolver.Container.Resolve<TTpe>(); 

而且这样的事情,是使用属性注入在这种情况下推荐的(可选的依赖),或是否有更好的方法?

+0

可选的依赖似乎是它可以是一个设计问题的迹象。如果一个对象有一个可选的依赖项,我猜它有多个函数,并且可能应该被分成两个或更多的内聚单元。 – MattDavey 2012-04-23 11:19:13

回答

3

调用从内部代码通常是一个坏主意的容器。 Mark Seemann有关于此的good article

物业注射本身是好的,但一般应只在两种情况下使用:

  1. 的依赖确实是可选的,并且应用程序可以正常工作丢失,当它。或者,
  2. 构造方法注入是不可能的,例如,由于循环依赖的。

在其他情况下,去构造函数注入。

做财产注射Munq的方法如下:

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

注意,依赖关系应该很少是可选的。可选依赖性使应用程序代码更加复杂,因为这会强制代码区分两种类型的依赖关系(实现和空值),并会导致代码中出现额外的if-null检查。大多数情况下,您可以简单地创建所需的依赖关系,并添加注入/注册空实现(Null Object Pattern)。

+0

我同意100%。在我的情况下,我正在尝试使用日志记录和缓存提供程序(如果可用,我想利用它们),但不希望在缺少时影响任何内容。 另外,谢谢你的文章。 – 2012-04-23 15:01:03

+1

相反optionaly注入那些依赖关系特性,更好的是使用构造器注入和注入空对象(空实现)可能时。这使得应用程序逻辑更加简单,因为您不必进行空检查,并且更清楚地知道类型需要哪些依赖关系。 – Steven 2012-04-23 17:19:25

+0

而在谈论日志时,您可能会发现这个答案很有趣:http://stackoverflow.com/a/9915056/264697。 – Steven 2012-04-23 17:23:05

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