话不多说,直入主题看我们的解决方案结构:
分别对上面的工程进行简单的说明:
1、TianYa.DotNetShare.Model:为demo的实体层
2、TianYa.DotNetShare.Repository:为demo的仓储层即数据访问层
3、TianYa.DotNetShare.Service:为demo的服务层即业务逻辑层
4、TianYa.DotNetShare.MvcDemo:为demo的web层项目,MVC框架
约定:本demo的web项目为ASP.NET Web 应用程序(.NET Framework 4.5) MVC框架,实体层、仓储层、服务层均为.NET Framework 4.5 类库。
1、定义一个空接口IDependency,后面有妙用,用于一次性注入
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TianYa.DotNetShare.Model { /// <summary> /// 空接口,用于一次性注入 /// </summary> public interface IDependency { } }
2、新建一个学生实体 Student
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TianYa.DotNetShare.Model { /// <summary> /// 学生类 /// </summary> public class Student { /// <summary> /// 学号 /// </summary> public string StuNo { get; set; } /// <summary> /// 姓名 /// </summary> public string Name { get; set; } /// <summary> /// 年龄 /// </summary> public int Age { get; set; } /// <summary> /// 性别 /// </summary> public string Sex { get; set; } } }
demo中的实体就这样了
本demo的仓储层需要引用我们的实体层TianYa.DotNetShare.Model
为什么选择用仓储,原因很简单,方便我们进行个性化扩展。在数据操作的底层进行其他个性化逻辑处理。
约定:
1、接口的定义放在根目录下,接口的实现类,统一放到Impl文件夹,表示实现类目录。
2、每个实体,对应一个仓储的接口和实现类,即有多少个实体,就对应创建多少个接口和实现类。
我们新建一个Student的仓储接口 IStudentRepository.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TianYa.DotNetShare.Model; namespace TianYa.DotNetShare.Repository { /// <summary> /// 学生类仓储层接口 /// </summary> public interface IStudentRepository { /// <summary> /// 根据学号获取学生信息 /// </summary> /// <param name="stuNo">学号</param> /// <returns>学生信息</returns> Student GetStuInfo(string stuNo); } }
接着在Impl中新建一个Student的仓储实现StudentRepository.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TianYa.DotNetShare.Model; namespace TianYa.DotNetShare.Repository.Impl { /// <summary> /// 学生类仓储层 /// </summary> public class StudentRepository : IStudentRepository, IDependency { /// <summary> /// 根据学号获取学生信息 /// </summary> /// <param name="stuNo">学号</param> /// <returns>学生信息</returns> public Student GetStuInfo(string stuNo) { //数据访问逻辑,此处为了演示就简单些 var student = new Student(); switch (stuNo) { case "10000": student = new Student() { StuNo = "10000", Name = "张三", Sex = "男", Age = 20 }; break; case "10001": student = new Student() { StuNo = "10001", Name = "钱七七", Sex = "女", Age = 18 }; break; case "10002": student = new Student() { StuNo = "10002", Name = "李四", Sex = "男", Age = 21 }; break; default: student = new Student() { StuNo = "10003", Name = "王五", Sex = "男", Age = 25 }; break; } return student; } } }
该类同时实现了IStudentRepository接口和IDependency接口,其中实现IDependency接口的目的是为了后面的web端进行一次性注入
本demo的服务层需要引用我们的实体层TianYa.DotNetShare.Model和我们的仓储层TianYa.DotNetShare.Repository
服务层与仓储层类似,它属于仓储层的使用者。定义的方式也与仓储层类似,有接口和Impl实现目录。
但服务层不需要一个实体对应一个,服务层更多的是按照功能模块进行划分,比如一个登录模块,创建一个LoginService。
为了演示,我们新建一个Student的服务层接口IStudentService.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TianYa.DotNetShare.Model; namespace TianYa.DotNetShare.Service { /// <summary> /// 学生类服务层接口 /// </summary> public interface IStudentService { /// <summary> /// 根据学号获取学生信息 /// </summary> /// <param name="stuNo">学号</param> /// <returns>学生信息</returns> Student GetStuInfo(string stuNo); } }
接着我们同样在Impl中新建一个Student的服务层实现StudentService.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TianYa.DotNetShare.Model; using TianYa.DotNetShare.Repository; namespace TianYa.DotNetShare.Service.Impl { /// <summary> /// 学生类服务层 /// </summary> public class StudentService : IStudentService, IDependency { /// <summary> /// 定义仓储层学生抽象类对象 /// </summary> protected IStudentRepository StuRepository; /// <summary> /// 空构造函数 /// </summary> public StudentService() { } /// <summary> /// 构造函数 /// </summary> /// <param name="stuRepository">仓储层学生抽象类对象</param> public StudentService(IStudentRepository stuRepository) { this.StuRepository = stuRepository; } /// <summary> /// 根据学号获取学生信息 /// </summary> /// <param name="stuNo">学号</param> /// <returns>学生信息</returns> public Student GetStuInfo(string stuNo) { var stu = StuRepository.GetStuInfo(stuNo); return stu; } } }
该类同时实现了IStudentService接口和IDependency接口,其中实现IDependency接口的目的是为了后面的web端进行一次性注入
本demo的web项目需要引用以下几个程序集:
1、TianYa.DotNetShare.Model 我们的实体层
2、TianYa.DotNetShare.Service 我们的服务层
3、TianYa.DotNetShare.Repository 我们的仓储层,正常我们的web项目是不应该使用仓储层的,此处我们引用是为了演示IOC依赖注入
4、Autofac 依赖注入基础组件
5、Autofac.Mvc5 依赖注入Mvc5的辅助组件
其中Autofac和Autofac.Mvc5可以从我们的NuGet上引用:
如果从线下已安装的程序包源中找不到我们要的程序集,可以尝试添加我们的程序包源,具体步骤如下:
名称:nuget.org 源:https://api.nuget.org/v3/index.json
添加新的包源后点击确定(如果已经有此包源就忽略该步骤),接下来就从NuGet上安装我们需要的程序集。
依次点击下载以下2个组件
至此我们所有的工作都已经准备好了,接下来就是重头戏,开始做注入工作了。
打开我们的Global.asax文件进行注入工作
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using Autofac; using Autofac.Integration.Mvc; using TianYa.DotNetShare.Model; using TianYa.DotNetShare.Repository.Impl; using TianYa.DotNetShare.Repository; namespace TianYa.DotNetShare.MvcDemo { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AutofacRegister(); //Autofac依赖注入 } /// <summary> /// Autofac依赖注入 /// </summary> private void AutofacRegister() { var builder = new ContainerBuilder(); //注册MVC控制器(注册所有到控制器,控制器注入,就是需要在控制器的构造函数中接收对象) builder.RegisterControllers(typeof(MvcApplication).Assembly); //构造函数注入,对StudentRepository与接口进行注入 builder.RegisterType<StudentRepository>().As<IStudentRepository>(); //设置依赖解析器 var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); } } }
至此,我们就完成了依赖注入工作,此方式为构造函数注入,接下来我们来看看控制器里面怎么弄。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TianYa.DotNetShare.Model; using TianYa.DotNetShare.Service; using TianYa.DotNetShare.Repository; namespace TianYa.DotNetShare.MvcDemo.Controllers { public class HomeController : Controller { /// <summary> /// 定义仓储层学生抽象类对象 /// </summary> protected IStudentRepository StuRepository; /// <summary> /// 通过构造函数进行注入 /// 注意:参数是抽象类,而非实现类,因为已经在Global.asax中将实现类映射给了抽象类 /// </summary> /// <param name="stuRepository">仓储层学生抽象类对象</param> public HomeController(IStudentRepository stuRepository) { this.StuRepository = stuRepository; } public ActionResult Index() { var stu = StuRepository.GetStuInfo("10000"); string msg = $"学号:10000,姓名:{stu.Name},性别:{stu.Sex},年龄:{stu.Age}。"; return Content(msg); } public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public ActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } } }
至此,完成处理,接下来就是见证奇迹的时刻了,我们访问 /home/index,看看是否能返回学生信息。
我们可以发现,返回了学生的信息,说明我们注入成功了。
总结:
1、采用的是构造函数注入的方式,在构造函数中初始化赋值。
2、StuRepository对象不需要实例化,即不需要new,降低了系统资源的消耗。
3、需要在Global.asax中对StudentRepository写映射,如果仓储类比较多的时候,就需要写很多了,如何避免,这个在后面的篇幅中会讲解到。
扩展: 上面讲解了构造函数注入的方式,下面扩展属性注入的方式,在Global.asax中稍微修改下注入语句即可。
将:
builder.RegisterControllers(typeof(MvcApplication).Assembly);
改为:
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
PropertiesAutowired:表示允许属性注入。
接下来我们来看下怎么使用,修改我们的Global.asax代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using Autofac; using Autofac.Integration.Mvc; using TianYa.DotNetShare.Model; using TianYa.DotNetShare.Repository.Impl; using TianYa.DotNetShare.Repository; using TianYa.DotNetShare.Service; using TianYa.DotNetShare.Service.Impl; namespace TianYa.DotNetShare.MvcDemo { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AutofacRegister(); //Autofac依赖注入 } /// <summary> /// Autofac依赖注入 /// </summary> private void AutofacRegister() { var builder = new ContainerBuilder(); //注册MVC控制器(注册所有到控制器,控制器注入,就是需要在控制器的构造函数中接收对象) builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired(); //构造函数注入,对StudentRepository与接口进行注入 builder.RegisterType<StudentRepository>().As<IStudentRepository>(); builder.RegisterType<StudentService>().As<IStudentService>(); //设置依赖解析器 var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); } } }
修改我们的控制器代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TianYa.DotNetShare.Model; using TianYa.DotNetShare.Service; using TianYa.DotNetShare.Repository; namespace TianYa.DotNetShare.MvcDemo.Controllers { public class HomeController : Controller { /// <summary> /// 定义仓储层学生抽象类对象 /// </summary> protected IStudentRepository StuRepository; /// <summary> /// 通过属性注入,访问修饰符必须为public,否则会注入失败 /// </summary> public IStudentService StuService { get; set; } /// <summary> /// 通过构造函数进行注入 /// 注意:参数是抽象类,而非实现类,因为已经在Global.asax中将实现类映射给了抽象类 /// </summary> /// <param name="stuRepository">仓储层学生抽象类对象</param> public HomeController(IStudentRepository stuRepository) { this.StuRepository = stuRepository; } public ActionResult Index() { var stu = StuRepository.GetStuInfo("10000"); var stu2 = StuService.GetStuInfo("10001"); string msg = $"学号:10000,姓名:{stu.Name},性别:{stu.Sex},年龄:{stu.Age}<br />"; msg += $"学号:10001,姓名:{stu2.Name},性别:{stu2.Sex},年龄:{stu2.Age}"; return Content(msg); } public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public ActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } } }
再访问一下我们的/home/index
我们可以发现,返回了学习信息,说明我们注入成功了。
另外通过这个例子我们可以发现在注入仓储层对象StudentRepository时,不仅控制器中注入成功了,而且在服务层中也注入成功了,说明我们的Autofac的注入是全局的。
总结:
1、通过属性注入,访问修饰符必须为public,否则会注入失败。
2、Autofac的注入是全局的。
未完待续......
参考博文:https://www.cnblogs.com/fei686868/p/10979790.html
版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!
ASP.NET MVC IOC依赖注入之Autofac系列(一)
原文:https://www.cnblogs.com/xyh9039/p/11318247.html