即依赖对象不在被依赖模块的类中直接经过new来获取web
先看看下面这段代码的问题~编程
public class SqlServerDal { public void Delete() { Console.WriteLine("删除表中某个订单信息!"); } } public class Order { private readonly SqlServerDal dal = new SqlServerDal(); public void Delete() { dal.Delete(); }
} using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DIPTest { class Program { static void Main(string[] args) { Order order = new Order(); order.Delete(); Console.Read(); } }
}
关于以上例子的说明:app
(1)在Order类中,它依赖于具体的对象SqlServerDal,违反了依赖倒置的原则,即不管是高层仍是底层,都应该依赖于抽象而不该该依赖于具体框架
(2)若是需求有变:数据访问层换为OracleDal,那么这个时候,就要修改Order类的代码;若是数据访问层再次换为MySqlDal,那么还要继续修改Order类的代码......若是无休止的变下去,将会是一个噩梦,并且你不但要修改 Order里边的代码,可能你还要修改Product、Users等类里边的代码,由于它们也可能跟Order类是一样的状况ide
怎么办呢?IOC啊~函数
那如何IOC啊?使用DI啊~ui
DI是IoC的一种实现方式,就是将依赖对象的建立和绑定转移到被依赖对象类的外面来实现this
依赖注入分为:构造函数注入、属性注入和接口注入spa
(1)构造函数注入code
首先,咱们为数据访问类SqlServerDal定义一个抽象接口IDataAccess,并在IDataAccess接口中声明GetAll方法:
public interface IDataAccess { void Delete(); }
而后在SqlServerDal类中,实现IDataAccess接口:
public class SqlServerDal:IDataAccess { public void Delete() { Console.WriteLine("删除表中某个订单信息!"); } }
接下来,咱们还须要修改Order类:
public class Order { private IDataAccess da; //构造函数注入 public Order(IDataAccess ida) { da = ida;
} public void Delete() { da.Delete(); } }
下面是控制台程序调用的代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text;
namespace IOCDemo
{ class Program { static void Main(string[] args) { SqlServerDal dal = new SqlServerDal();//在Order类外部建立依赖对象 Order order = new Order(dal);//经过构造函数注入依赖 order.Delete(); Console.Read(); } }
}
(2)属性注入
属性注入就是经过属性来传递依赖。所以,咱们首先须要在依赖类Order中定义一个属性:
public class Order { private IDataAccess _da;
//属性,接受依赖 public IDataAccess da { set { _da = value; } get { return _da; } } public void Delete() {
_da.Delete(); } }
下面是控制台程序调用的代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text;
namespace IOCDemo
{ class Program { static void Main(string[] args) { AccessDal dal = new AccessDal();//在外部建立依赖对象 Order order = new Order(); order.da = dal;//给属性赋值 order.Delete(); Console.Read(); } }
}
(3)接口注入
相比构造函数注入和属性注入,用起来没有它们方便。首先定义一个接口,包含一个设置依赖的方法。
public interface IDependent { void SetDependence(IDataAccess ida);//设置依赖项 }
用依赖类实现这个接口:
public class Order : IDependent { private IDataAccess _ida;
public void SetDependence(IDataAccess ida) { _ida = ida; } public void Delete() { _ida.Delete(); } }
下面是控制台程序调用的代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace IOCDemo { class Program { static void Main(string[] args) { AccessDal dal = new AccessDal();//在Order外部建立依赖对象 Order order = new Order(); order.SetDependence(dal);//传递依赖 order.Delete(); Console.Read(); } }
}
前面全部的栗子中,咱们都是经过手动的方式来建立依赖对象,并将引用传递给被依赖模块。好比:
SqlServerDal dal = new SqlServerDal();//在Order外部建立依赖对象 Order order = new Order(dal);//经过构造函数注入依赖
对于大型项目来讲,相互依赖的组件比较多。若是还用手动的方式,本身来建立和注入依赖的话,显然效率很低,并且每每还会出现不可控的场面。所以,IoC容器就诞生了。IoC容器其实是一个DI框架,它能简化咱们的工做量。它包含如下几个功能:
本篇咱们使用微软框架组给提供的Unity来实现依赖注入,它是最流行的IOC容器之一
Unit是微软patterns& practices组用C#实现的轻量级、可扩展的依赖注入容器,咱们能够经过代码或者XML配置文件的形式来配置对象与对象之间的关系,在运行时直接调用Unity容器便可获取咱们所需的对象,以便创建松散耦合的应用程序。
对于小型项目:用代码的方式实现便可
对于中大型项目:使用配置文件比较好
您能够访问http://unity.codeplex.com/releases获得最新版本的Unity,也能够直接在Nuget中获取到最新版本的Unity,或者下载微软的企业库,而后在项目中添加Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll的引用
这里用到的最重要的东东就是IUnityContainer 接口,它自己定义了不少方法,固然还有一些扩展方法,具体的接口定义这里就不说了,咱们会常常用到IUnityContainer 接口的RegisterInstance、RegisterType、Resolve等方法。
这里我举个栗子,首先定义以下接口,并用两个类来进行实现:
/// <summary> /// 班级接口 /// </summary> public interface IClass { string ClassName { get; set; } void ShowInfo(); } /// <summary> /// 计科班 /// </summary> public class CbClass : IClass { public string ClassName { get; set; } public void ShowInfo() { Console.WriteLine("计科班:{0}", ClassName); } } /// <summary> /// 电商班 /// </summary> public class EcClass : IClass { public string ClassName { get; set; } public void ShowInfo() { Console.WriteLine("电商班:{0}", ClassName); } }
(1)用编程方式实现注入
使用Unity来管理对象与对象之间的关系能够分为如下几步:
A、建立一个UnityContainer对象
B、经过UnityContainer对象的RegisterType方法来注册对象与对象之间的关系
C、经过UnityContainer对象的Resolve方法来获取指定对象关联的对象
注入代码以下:
public static void ContainerCodeTest() { IUnityContainer container = new UnityContainer(); //默认注册(无命名),若是后面还有默认注册会覆盖前面的 container.RegisterType<IClass, CbClass>(); //命名注册 container.RegisterType<IClass, EcClass>("ec"); //解析默认对象 IClass cbClass = container.Resolve<IClass>(); cbClass.ShowInfo(); //指定命名解析对象 IClass ecClass = container.Resolve<IClass>("ec"); ecClass.ShowInfo(); //获取容器中全部IClass的注册的已命名对象 IEnumerable<IClass> classList = container.ResolveAll<IClass>(); foreach (var item in classList) { item.ShowInfo(); } }
(2)配置文件方式
经过配置文件配置Unity信息须要有如下几个步骤:
A、在配置文件<configSections> 配置节下注册名为unity的section
B、在<configuration> 配置节下添加Unity配置信息
C、在代码中读取配置信息,并将配置载入到UnityContainer中
配置文件内容以下:
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/> </configSections> <unity xmlns=http://schemas.microsoft.com/practices/2010/unity> <!--定义类型别名--> <aliases> <add alias="IClass" type="ConsoleApplication1.UnityDemo.IClass,ConsoleApplication1" /> <add alias="CbClass" type="ConsoleApplication1.UnityDemo.CbClass,ConsoleApplication1" /> <add alias="EcClass" type="ConsoleApplication1.UnityDemo.EcClass,ConsoleApplication1" /> </aliases> <!--容器--> <container name="FirstClass"> <!--映射关系--> <register type="IClass" mapTo="CbClass"></register> <register type="IClass" mapTo="EcClass" name="ec"></register> </container> </unity> </configuration>
注入代码以下:
public static void ContainerConfiguration() { IUnityContainer container = new UnityContainer();//获取指定名称的配置节
UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); container.LoadConfiguration(section, "FirstClass");//获取特定配置节下已命名的配置节<container name="FirstClass">下的配置信息
IClass classInfo = container.Resolve<IClass>("ec"); classInfo. ShowInfo(); }
注意:
若是系统比较庞大,那么对象之间的依赖关系可能就会很复杂,最终致使配置文件变得很大,因此咱们须要将Unity的配置信息从App.config或web.config中分离出来到某一个单独的配置文件中,好比Unity.config,而后将其做为参数传递给下面的方法,依然能够实现依赖注入:
public static void ContainerConfigurationFromFile(string configFile) { //根据文件名获取指定config文件 var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = configFile }; //从config文件中读取配置信息 Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); var unitySection = (UnityConfigurationSection)configuration.GetSection("unity"); var container = new UnityContainer().LoadConfiguration(unitySection, "FirstClass"); IClass classInfo = container.Resolve<IClass>("ec"); classInfo.ShowInfo(); }
在平常开发的过程当中咱们有时候会本身建立好一个对象,可是你又想对这个已经建立好的对象的生命周期进行管理,这个时候你可使用Unity提供的RegisterInstance方法(有不少重载),因为RegisterInstance是对已存在的实例进行注册,因此没法经过配置文件来进行配置。
代码示例以下:
public static void RegisterInstance() { IClass myClass = new MyClass(); IClass yourClass = new YourClass(); //为myClass实例注册默认实例 container.RegisterInstance<IClass>(myClass); //为yourClass实例注册命名实例,同RegisterType container.RegisterInstance<IClass>("yourInstance", yourClass); container.Resolve<IClass>().ShowInfo(); container.Resolve<IClass>("yourInstance").ShowInfo(); }
这段代码很简单,就是使用RegisterInstance方法将已存在的实例myClass、yourClass等注册到UnityContainer中,默认状况下其实用的是ContainerControlledLifetimeManager,这个生命周期是由UnityContainer来进行管理,UnityContainer会维护一个对象实例的强引用,当你将已存在的实例注册到UnityContainer后,每次经过Resolve方法获取对象都是同一对象,也就是单件实例(singleton instance),具体有关生命周期相关信息在下面进行介绍。
注意是单实例哦~
咱们在系统中引入Unity主要就是想经过Unity来解除对象之间的依赖关系,方便咱们根据配置调用到所需的对象,而Unity默认状况下会自动帮咱们维护好这些对象的生命周期,可能Unity自动维护的生命周期并不老是咱们想要的,这时咱们就要根据具体的需求来更改这些对象的生命周期,下面就简单介绍一下Unity中内置的两个经常使用生命周期管理器,其余的生命周期管理器若是须要能够本身上网查看其详细说明。
(1)TransientLifetimeManager,瞬态生命周期,默认状况下,在使用RegisterType进行对象关系注册时若是没有指定生命周期管理器则默认使用这个生命周期管理器,这个生命周期管理器就如同其名字同样,当使用这种管理器的时候,每次经过Resolve或ResolveAll调用对象的时候都会从新建立一个新的对象。
代码以下:
public static void TransientLifetimeManagerCode() { //如下2种注册效果是同样的 container.RegisterType<IClass, MyClass>(); container.RegisterType<IClass, MyClass>(new TransientLifetimeManager()); Console.WriteLine("-------TransientLifetimeManager Begin------"); Console.WriteLine("第一次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第二次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("-------TransientLifetimeManager End------"); }
若是是使用配置的方式,则须要在配置文件中注册关系的时候在<register>配置节下新增<lifetime>既可(若是不新增则默认使用TransientLifetimeManager),若是想使用其余的生命周期管理器,则更改此配置节便可!
其中<lifetime>有3个参数:
若是用今生命周期管理器,则要在配置文件中新增的节点以下:
<register type="IClass" mapTo="MyClass"> <lifetime type="transient" /> </register>
注入代码以下:
public static void TransientLifetimeManagerConfiguration() { //获取指定名称的配置节 UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); container.LoadConfiguration(section, "FirstClass"); Console.WriteLine("-------TransientLifetimeManager Begin------"); Console.WriteLine("第一次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>("transient").GetHashCode()); Console.WriteLine("第二次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>("transient").GetHashCode()); Console.WriteLine("-------TransientLifetimeManager End------"); }
以上不管是代码仍是配置的方式,运行以后都会发现实例的哈希码是不同的,说明每次调用都是从新生成一个对象实例!
(2)ContainerControlledLifetimeManager,容器控制生命周期管理,这个生命周期管理器是RegisterInstance默认使用的生命周期管理器,也就是单件实例,UnityContainer会维护一个对象实例的强引用,每次调用的时候都会返回同一对象,示例代码以下:
public static void ContainerControlledLifetimeManagerCode() { IClass myClass = new MyClass(); //如下2种注册效果是同样的 container.RegisterInstance<IClass>("ccl", myClass); container.RegisterInstance<IClass>("ccl", myClass, new ContainerControlledLifetimeManager()); container.RegisterType<IClass, MyClass>(new ContainerControlledLifetimeManager()); Console.WriteLine("-------ContainerControlledLifetimeManager Begin------"); Console.WriteLine("第一次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第二次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第一次调用RegisterInstance注册的对象HashCode:" + container.Resolve<IClass>("ccl").GetHashCode()); Console.WriteLine("第二次调用RegisterInstance注册的对象HashCode:" + container.Resolve<IClass>("ccl").GetHashCode()); Console.WriteLine("-------ContainerControlledLifetimeManager End------"); }
运行以后都会发现实例的哈希码是同样的,说明是单实例的
若是用今生命周期管理器,则要在配置文件中新增的节点以下:
<register type="IClass" mapTo="MyClass" name="ccl"> <lifetime type="singleton" /> </register>
注入代码与上例相似,这里再也不列出
说了这么多Unity,主要仍是想将其用到ASP.NET MVC的IOC中,其实很简单,大概就几个步骤搞定:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Microsoft.Practices.Unity; namespace UnityOfMVC.IOC { public class UnityDependencyResolver : IDependencyResolver { IUnityContainer container; public UnityDependencyResolver(IUnityContainer container) { this.container = container; } public object GetService(Type serviceType) { if (!this.container.IsRegistered(serviceType)) { return null; } return container.Resolve(serviceType); } public IEnumerable<object> GetServices(Type serviceType) { return container.ResolveAll(serviceType); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; using System.Web.SessionState; namespace UnityOfMVC.IOC { public class UnityControllerFactory : DefaultControllerFactory { IUnityContainer container; public UnityControllerFactory(IUnityContainer container) { this.container = container; } protected override IController GetControllerInstance(RequestContext reqContext, Type controllerType) { return container.Resolve(controllerType) as IController; } } }
<configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" /> </configSections> <unity> <containers> <container name="defaultContainer"> <register type="UnityOfMVC.Models.IStudentRepository, UnityOfMVC" mapTo="UnityOfMVC.Models.StudentRepository, UnityOfMVC"/> <register type="System.Web.Mvc.IControllerFactory, System.Web.Mvc" mapTo="UnityOfMVC.IOC.UnityControllerFactory, UnityOfMVC"/> </container> </containers> </unity>
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Configuration; using System.Web.Mvc; using Microsoft.Practices.Unity; using Microsoft.Practices.Unity.Configuration; using UnityOfMVC.IOC; namespace UnityOfMVC.BootStrapper { public class Bootstrapper { public static IUnityContainer Init() { var container = BuildUnityContainer(); DependencyResolver.SetResolver(new UnityDependencyResolver(container)); return container; } private static IUnityContainer BuildUnityContainer() { var container = new UnityContainer(); UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName); configuration.Configure(container, "defaultContainer"); return container; } } }
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth();
BootStrapper.Bootstrapper.Init(); //就是这个东东
} }
(1)首先声明一个Student学生类
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace UnityOfMVC.Models { public class Student { public int Id { get; set; } public string Name { get; set; } public string Graduation { get; set; } public string School { get; set; } public string Major { get; set; } } }
(2)而后声明仓储接口和其实现
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace UnityOfMVC.Models { public interface IStudentRepository { IEnumerable<Student> GetAll(); Student Get(int id); Student Add(Student item); bool Update(Student item); bool Delete(int id); } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace UnityOfMVC.Models { public class StudentRepository : IStudentRepository { private List<Student> Articles = new List<Student>(); public StudentRepository() { //添加演示数据 Add(new Student { Id = 1, Name = "张三", Major = "软件工程", Graduation = "2013年", School = "西安工业大学" }); Add(new Student { Id = 2, Name = "李四", Major = "计算机科学与技术", Graduation = "2013年", School = "西安工业大学" }); Add(new Student { Id = 3, Name = "王五", Major = "自动化", Graduation = "2013年", School = "西安工业大学" }); } /// <summary> /// 获取所有文章 /// </summary> /// <returns></returns> public IEnumerable<Student> GetAll() { return Articles; } /// <summary> /// 经过ID获取文章 /// </summary> /// <param name="id"></param> /// <returns></returns> public Student Get(int id) { return Articles.Find(p => p.Id == id); } /// <summary> /// 添加文章 /// </summary> /// <param name="item"></param> /// <returns></returns> public Student Add(Student item) { if (item == null) { throw new ArgumentNullException("item"); } Articles.Add(item); return item; } /// <summary> /// 更新文章 /// </summary> /// <param name="item"></param> /// <returns></returns> public bool Update(Student item) { if (item == null) { throw new ArgumentNullException("item"); } int index = Articles.FindIndex(p => p.Id == item.Id); if (index == -1) { return false; } Articles.RemoveAt(index); Articles.Add(item); return true; } /// <summary> /// 删除文章 /// </summary> /// <param name="id"></param> /// <returns></returns> public bool Delete(int id) { Articles.RemoveAll(p => p.Id == id); return true; } } }
(3)最后添加控制器StudentController,并注入依赖代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using UnityOfMVC.Models; namespace UnityOfMVC.Controllers { public class StudentController : Controller { readonly IStudentRepository repository; //构造器注入 public StudentController(IStudentRepository repository) { this.repository = repository; } public ActionResult Index() { var data = repository.GetAll(); return View(data); } } }
(4)最后为控制器StudentController的Index方法添加视图便可,这里再也不详述,运行效果以下: