在我们的领域驱动设计(DDD)开发中,我们经常需要IOC框架使得我的框架依赖翻转,依赖抽象,避免直接new依赖于我们的具体实现。这些使得我们的框架整个项目结构不变,很方便的改变具体实现,使得项目提供可测试性,模块之间实现高内聚低耦合,减少我们的后期维护成本。IOC框架一般基于容器,在容器中存储着各个抽象和具体实现的依赖关系,当我们需要发出请求的时候,IOC框架会在当前容器中找到我们所需要的具体实现返回给我们,当然这里还有DI注入(属性,方法,构造),在我们的使用者(客户端)不需要了解具体实现,如何初始化,如何流转等具体,只需明白我们的契约接口暴露给我们的服务,IOC框架是解决抽象和具体直接的创建问题。其他资料可以参见Inversion of Control Containers and the Dependency Injection pattern

     当然Unity框架中为我们提供了RegisterInstance,RegisterType方法我们可以在代码中注册到容器,比如NLayerApp中就在IoCFactory中注册一大堆抽象-具体关联。但是在我们的实际实践中一般会选择另一种方式xml配置配置,因为这样我们会得到更大的灵活性,需求变化只要抽象接口不变,我们也只需要在xml配置文件中修改一行配置加入我们的具体实现,加入我们的程序集,就可以适应需求变化,这更满足oo设计“开闭原则”。

   在这里个人实践利用抽象(接口)定义Attribute制定具体ConfigFile(配置文件),Container(容器),Name(名称)解决IOC植入,减少我们多次去读取配置文件。Unity为我们提供了在Web.config,App.config中配置注入信息,或者注册外部配置,但是很多时候我们更希望,在我们的 不同模块下,应用不同的IOC配置信息,这些可以减少维护的关联少些,清晰,同时文件夹的出现便于我们的配置信息的管理。

Attribute实现:

  1. UnityInjectionAttributeView Code   
  2.  
  3. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]   
  4.    public class UnityInjectionAttribute : Attribute   
  5.    {   
  6.  
  7.        public UnityInjectionAttribute(string Container)   
  8.        {   
  9.            this.Container = Container;              
  10.        }   
  11.  
  12.        public string Container   
  13.        {   
  14.            get;   
  15.            set;   
  16.        }   
  17.  
  18.        public string ConfigFile   
  19.        {   
  20.            get;   
  21.            set;   
  22.        }   
  23.  
  24.        public string Name   
  25.        {   
  26.            get;   
  27.            set;   
  28.        }   
  29.  
  30.        public Microsoft.Practices.Unity.Configuration.UnityConfigurationSection GetUnityConfigurationSection()   
  31.        {   
  32.            if (!string.IsNullOrEmpty(this.ConfigFile))   
  33.            {   
  34.                var fileMap = new System.Configuration.ExeConfigurationFileMap { ExeConfigFilename = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, this.ConfigFile) };   
  35.                System.Configuration.Configuration configuration = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, System.Configuration.ConfigurationUserLevel.None);   
  36.                return configuration == null ? null : configuration.GetSection(Microsoft.Practices.Unity.Configuration.UnityConfigurationSection.SectionName) as Microsoft.Practices.Unity.Configuration.UnityConfigurationSection;   
  37.            }   
  38.            return System.Configuration.ConfigurationManager.GetSection(Microsoft.Practices.Unity.Configuration.UnityConfigurationSection.SectionName) as Microsoft.Practices.Unity.Configuration.UnityConfigurationSection;   
  39.        }   
  40.    }   
  41. 复制代码 

在这里我们GetUnityConfigurationSection根据ConfigFile获取UnityConfigurationSection ,ConfigFile为空则当前应用配置文件,不空则为路径。在这里我们为了性能,减少过多的IOC操作,读取配置文件,我们可以更具具体需要加入对配置文件UnityConfigurationSection的缓存(ConfigFile作为key,UnityConfigurationSection为value )。

   同时提供操作辅助方法:ELUnityUtility

  1. View Code   
  2.  
  3. public static class ELUnityUtility   
  4.    {   
  5.        public static T Resolve<T>() where T : class   
  6.        {   
  7.            return Resolve(typeof(T)) as T;   
  8.        }   
  9.  
  10.        public static object Resolve(this Type type)   
  11.        {   
  12.            var attrs = type.GetCustomAttributes(typeof(Utils.UnityInjectionAttribute), trueas Utils.UnityInjectionAttribute[];   
  13.            if (attrs != null && attrs.Length > 0)   
  14.            {   
  15.                var attr = attrs[0];   
  16.                var unitySection = attr.GetUnityConfigurationSection();   
  17.                if (unitySection != null)   
  18.                {   
  19.                    var container = new Microsoft.Practices.Unity.UnityContainer().LoadConfiguration(unitySection, string.IsNullOrEmpty(attr.Container) ? unitySection.Containers.Default.Name : attr.Container);   
  20.                    var obj = string.IsNullOrEmpty(attr.Name) ? container.Resolve(type) : container.Resolve(type, attr.Name);   
  21.                    if (obj != null)   
  22.                    {   
  23.                        var piabAtttr = obj.GetType().GetCustomAttributes(typeof(ELPolicyinjectionAttribute), falseas ELPolicyinjectionAttribute[];   
  24.                        if (piabAtttr.Length > 0)   
  25.                        {   
  26.                            obj = Microsoft.Practices.EnterpriseLibrary.PolicyInjection.PolicyInjection.Wrap(type, obj);   
  27.                        }   
  28.                        return obj;   
  29.                    }   
  30.                }   
  31.            }   
  32.            return null;   
  33.        }   
  34.  
  35.        public static IEnumerable<T> ResolveAll<T>() where T : class   
  36.        {   
  37.            return ResolveAll(typeof(T)) as IEnumerable<T>;   
  38.        }   
  39.  
  40.        public static object ResolveAll(this Type type)   
  41.        {   
  42.            var attrs = type.GetCustomAttributes(typeof(Utils.UnityInjectionAttribute), trueas Utils.UnityInjectionAttribute[];   
  43.            if (attrs != null && attrs.Length > 0)   
  44.            {   
  45.                var attr = attrs[0];   
  46.                var unitySection = attr.GetUnityConfigurationSection();   
  47.                if (unitySection != null)   
  48.                {   
  49.                    var container = new Microsoft.Practices.Unity.UnityContainer().LoadConfiguration(unitySection, string.IsNullOrEmpty(attr.Container) ? unitySection.Containers.Default.Name : attr.Container);   
  50.                    return container.ResolveAll(type);   
  51.                }   
  52.            }   
  53.            return null;   
  54.        }   
  55.  
  56.    }  
  57. 复制代码 

这里我们就可以很简便的获取IOC翻转。注:这里还有根据具体实现是否具体ELPolicyinjectionAttribute来决定是否进行PIAB的AOP操作,当然我们也可以在Unity配置文件中引入节点扩展

Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension,
                    Microsoft.Practices.Unity.Interception.Configuration

(PIAB利用的是透明代理速度较慢所以一般很少使用,当然你也可以实现具体的PIAB AOP方式比如注入MSIL,但我们已经有了很多注入MSIL的AOP框架了,我不准备去造轮子),ELPolicyinjectionAttribute:

  1. View Code   
  2.  
  3. [AttributeUsage(AttributeTargets.Class)]   
  4.    public class ELPolicyinjectionAttribute : Attribute   
  5.    {   
  6.        public string Name   
  7.        {   
  8.            get;   
  9.            set;   
  10.        }   
  11.    }  
  12. 复制代码 

这样:我们的客户端 就可以很简单的使用了:

  1. View Code   
  2.  
  3. class Program   
  4.    {   
  5.        static void Main(string[] args)   
  6.        {   
  7.  
  8.            ELUnityUtility.Resolve<IClass2>().Show();   
  9.            (typeof(IClass2).Resolve() as IClass2).Show();   
  10.            Console.Read();   
  11.        }   
  12.    }   
  13.  
  14.    public interface IClass1   
  15.    {   
  16.        void Show();   
  17.    }   
  18.  
  19.    [Green.Utils.ELPolicyinjection]   
  20.    public class Class1 : IClass1   
  21.    {   
  22.  
  23.        #region IClass1 成员   
  24.        [TestCallHandler]   
  25.        public void Show()   
  26.        {   
  27.            Console.WriteLine(this.GetType());   
  28.        }   
  29.  
  30.        #endregion   
  31.    }   
  32.  
  33.    [Green.Utils.UnityInjection("First"Name = "class2", ConfigFile = "App1.config")]   
  34.    public interface IClass2   
  35.    {   
  36.        void Show();   
  37.    }   
  38.  
  39.     public class Class2 : ConsoleApplication1.IClass2   
  40.    {   
  41.        [Microsoft.Practices.Unity.Dependency("class1")]   
  42.        public IClass1 Class1   
  43.        {   
  44.            get;   
  45.            set;   
  46.        }   
  47.  
  48.             public void Show()   
  49.        {   
  50.            Console.WriteLine(this.GetType());   
  51.            Class1.Show();   
  52.        }   
  53.    }     
  54. 复制代码 

App1.Config配置:

  1. View Code   
  2.  
  3. <?xml version="1.0" encoding="utf-8" ?>   
  4. <configuration>   
  5.   <configSections>   
  6.     <section name="unity"   
  7.              type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,   
  8.              Microsoft.Practices.Unity.Configuration"/>   
  9.   </configSections>   
  10.   <unity xmlns="http://schemas.microsoft.com/practices/2010/unity%22>   
  11.     <container name="First">   
  12.       <register type="ConsoleApplication1.IClass1,ConsoleApplication1" mapTo="ConsoleApplication1.Class1,ConsoleApplication1" name="class1" />   
  13.       <register type="ConsoleApplication1.IClass2,ConsoleApplication1" mapTo="ConsoleApplication1.Class2,ConsoleApplication1" name="class2"  />   
  14.     </container>   
  15.   </unity>   
  16. </configuration>  
  17. 复制代码 

下边是一个完整的带PIAB的例子:

  1. View Code   
  2.  
  3. using System;   
  4. using System.Collections.Generic;   
  5. using System.Linq;   
  6. using System.Text;   
  7. using Green.Utils;   
  8. using Microsoft.Practices.Unity.InterceptionExtension;   
  9. using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;   
  10.  
  11. namespace ConsoleApplication1   
  12. {   
  13.     class Program   
  14.     {   
  15.         static void Main(string[] args)   
  16.         {   
  17.  
  18.             ELUnityUtility.Resolve<IClass2>().Show();   
  19.             (typeof(IClass2).Resolve() as IClass2).Show();   
  20.             Console.Read();   
  21.         }   
  22.     }   
  23.  
  24.     public interface IClass1   
  25.     {   
  26.         void Show();   
  27.     }   
  28.  
  29.     [Green.Utils.ELPolicyinjection]   
  30.     public class Class1 : IClass1   
  31.     {   
  32.  
  33.         #region IClass1 成员   
  34.         [TestCallHandler]   
  35.         public void Show()   
  36.         {   
  37.             Console.WriteLine(this.GetType());   
  38.         }   
  39.  
  40.         #endregion   
  41.     }   
  42.  
  43.     [Green.Utils.UnityInjection("First"Name = "class2", ConfigFile = "App1.config")]   
  44.     public interface IClass2   
  45.     {   
  46.         void Show();   
  47.     }   
  48.  
  49.     [Green.Utils.ELPolicyinjection]   
  50.     public class Class2 : ConsoleApplication1.IClass2   
  51.     {   
  52.         [Microsoft.Practices.Unity.Dependency("class1")]   
  53.         public IClass1 Class1   
  54.         {   
  55.             get;   
  56.             set;   
  57.         }   
  58.  
  59.         [TestCallHandler]   
  60.         public void Show()   
  61.         {   
  62.             Console.WriteLine(this.GetType());   
  63.             Class1.Show();   
  64.         }   
  65.     }   
  66.  
  67.     [Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationElementType(typeof(CustomCallHandlerData))]   
  68.     public class TestCallHandler : ICallHandler   
  69.     {   
  70.         #region ICallHandler 成员   
  71.  
  72.         public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)   
  73.         {   
  74.             if (input == null) throw new ArgumentNullException("input");   
  75.             if (getNext == null) throw new ArgumentNullException("getNext");   
  76.             Console.WriteLine("begin....");   
  77.             var result = getNext()(input, getNext);   
  78.             Console.WriteLine("end....");   
  79.             return result;   
  80.         }   
  81.  
  82.         public int Order   
  83.         {   
  84.             get;   
  85.             set;   
  86.         }   
  87.  
  88.         #endregion   
  89.     }   
  90.  
  91.     [AttributeUsage(AttributeTargets.Method)]   
  92.     public class TestCallHandlerAttribute : HandlerAttribute   
  93.     {   
  94.         public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)   
  95.         {   
  96.             return new TestCallHandler();   
  97.         }   
  98.     }   
  99. }  
  100. 复制代码 

欢迎大家指正,批评,交流是的大家都功能进步。代码下载