ibcadmin 发表于 2019-8-13 19:00:25

ASP.NET MVC IOC依赖注入之Autofac系列(一)- MVC当中应用

<p>话不多说,直入主题看我们的解决方案结构:</p>
<p><div align="center"></div></p>
<p>分别对上面的工程进行简单的说明:</p>
<p>1、TianYa.DotNetShare.Model:为demo的实体层</p>
<p>2、TianYa.DotNetShare.Repository:为demo的仓储层即数据访问层</p>
<p>3、TianYa.DotNetShare.Service:为demo的服务层即业务逻辑层</p>
<p>4、TianYa.DotNetShare.MvcDemo:为demo的web层项目,MVC框架</p>
<p>约定:本demo的web项目为ASP.NET Web 应用程序(.NET Framework 4.5) MVC框架,实体层、仓储层、服务层均为.NET Framework 4.5 类库。</p>
<h3>一、实体层</h3>
<p><div align="center"></div></p>
<p>1、定义一个空接口IDependency,后面有妙用,用于一次性注入</p>

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

<p>2、新建一个学生实体 Student</p>

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

<p>demo中的实体就这样了</p>
<h3>二、仓储层</h3>
<p><div align="center"></div></p>
<p>本demo的仓储层必要引用我们的实体层TianYa.DotNetShare.Model</p>
<p>为什么选择用仓储,原因很简单,方便我们进行个性化扩展。在数据操作的底层进行其他个性化逻辑处理。</p>
<p>约定:</p>
<p>1、接口的定义放在根目录下,接口的实现类,同一放到Impl文件夹,表示实现类目录。</p>
<p>2、每个实体,对应一个仓储的接口和实现类,即有多少个实体,就对应创建多少个接口和实现类。</p>
<p>我们新建一个Student的仓储接口 IStudentRepository.cs</p>

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

<p>接着在Impl中新建一个Student的仓储实现StudentRepository.cs</p>

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

<p>该类同时实现了IStudentRepository接口和IDependency接口,其中实现IDependency接口的目的是为了后面的web端进行一次性注入</p>
<h3>三、服务层</h3>
<p><div align="center"></div></p>
<p>本demo的服务层必要引用我们的实体层TianYa.DotNetShare.Model和我们的仓储层TianYa.DotNetShare.Repository</p>
<p>服务层与仓储层雷同,它属于仓储层的使用者。定义的方式也与仓储层雷同,有接口和Impl实现目录。</p>
<p>但服务层不必要一个实体对应一个,服务层更多的是按照功能模块进行划分,比如一个登录模块,创建一个LoginService。</p>
<p>为了演示,我们新建一个Student的服务层接口IStudentService.cs</p>

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

<p>接着我们同样在Impl中新建一个Student的服务层实现StudentService.cs</p>

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

<p>该类同时实现了IStudentService接口和IDependency接口,其中实现IDependency接口的目的是为了后面的web端进行一次性注入</p>
<h3>四、Web层</h3>
<p><div align="center"></div></p>
<p>本demo的web项目必要引用以下几个程序集:</p>
<p>1、TianYa.DotNetShare.Model 我们的实体层</p>
<p>2、TianYa.DotNetShare.Service 我们的服务层</p>
<p>3、TianYa.DotNetShare.Repository 我们的仓储层,正常我们的web项目是不应该使用仓储层的,此处我们引用是为了演示IOC依赖注入</p>
<p>4、Autofac 依赖注入基础组件</p>
<p>5、Autofac.Mvc5 依赖注入Mvc5的辅助组件</p>
<p>其中Autofac和Autofac.Mvc5可以从我们的NuGet上引用:</p>
<p><div align="center"></div></p>
<p>如果从线下已安装的程序包源中找不到我们要的程序集,可以尝试添加我们的程序包源,具体步骤如下:</p>
<p><div align="center"></div></p>
<p><div align="center"></div></p>

名称:nuget.org
源:https://api.nuget.org/v3/index.json

<p>添加新的包源后点击确定(如果已经有此包源就忽略该步骤),接下来就从NuGet上安装我们必要的程序集。</p>
<p><div align="center"></div></p>
<p>依次点击下载以下2个组件</p>
<p><div align="center"></div></p>
<p>到了这里我们全部的工作都已经准备好了,接下来就是重头戏,开始做注入工作了。</p>
<p>打开我们的Global.asax文件进行注入工作</p>

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

<p>至此,我们就完成了依赖注入工作,此方式为构造函数注入,接下来我们来看看控制器内里怎么弄:</p>

using System.Web.Mvc;
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);
      }
    }
}

<p>至此,完成处理,接下来就是见证奇迹的时刻了,我们访问 /home/index,看看是否能返回学生信息。</p>
<p><div align="center"></div></p>
<p>我们可以发现,返回了学生的信息,说明我们注入成功了。</p>
<p>总结:</p>
<p>1、采用的是构造函数注入的方式,在构造函数中初始化赋值。</p>
<p>2、StuRepository对象不必要实例化,即不必要new,降低了系统资源的消耗。</p>
<p>3、必要在Global.asax中对StudentRepository写映射,如果仓储类比较多的时候,就必要写很多了,如何避免,这个在后面的篇幅中会讲解到。</p>
<p>扩展: 上面讲解了构造函数注入的方式,下面扩展属性注入的方式,在Global.asax中稍微修改下注入语句即可。</p>
<p>将:</p>

builder.RegisterControllers(typeof(MvcApplication).Assembly);

<p>改为:</p>

builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();

<p>PropertiesAutowired:表示答应属性注入。</p>
<p>接下来我们来看下怎么使用,修改下我们的依赖注入语句,新增服务层学生类注入:</p>

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

<p>修改我们的控制器代码:</p>

using System.Web.Mvc;
using TianYa.DotNetShare.Service;

namespace TianYa.DotNetShare.MvcDemo.Controllers
{
    public class HomeController : Controller
    {
      /// <summary>
      /// 通过属性注入,访问修饰符必须为public,否则会注入失败
      /// </summary>
      public IStudentService StuService { get; set; }

      public ActionResult Index()
      {
            var stu = StuService.GetStuInfo("10001");
            string msg = $"学号:10001,姓名:{stu.Name},性别:{stu.Sex},年龄:{stu.Age}。";
            return Content(msg);
      }
    }
}

<p>再访问一下我们的/home/index</p>
<p><div align="center"></div></p>
<p>我们可以发现,返回了学习信息,说明我们注入成功了。</p>
<p>另外通过这个例子我们可以发现在注入仓储层对象StudentRepository时,不仅控制器中注入成功了,而且在服务层中也注入成功了,说明我们的Autofac的注入是全局的。</p>
<p>总结:</p>
<p>1、通过属性注入,访问修饰符必须为public,否则会注入失败。</p>
<p>2、Autofac的注入是全局的。</p>
<p>扩展:以上例子我们都是将实现类映射给对应的实现类接口,那能不能直接注入实现类呢,答案是可以的。</p>
<p>修改下我们的依赖注入,新增我们的实现类注入语句:</p>

/// <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>();
    builder.RegisterType<StudentRepository>(); //实现类注入

    //设置依赖解析器
    var container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}

<p>再来看下我们的控制器</p>

using System.Web.Mvc;
using TianYa.DotNetShare.Repository.Impl;

namespace TianYa.DotNetShare.MvcDemo.Controllers
{
    public class HomeController : Controller
    {
      /// <summary>
      /// 定义仓储层学生实现类对象
      /// </summary>
      public StudentRepository StuRepositoryImpl { get; set; }

      public ActionResult Index()
      {
            var stu = StuRepositoryImpl.GetStuInfo("10002");
            string msg = $"学号:10002,姓名:{stu.Name},性别:{stu.Sex},年龄:{stu.Age}";
            return Content(msg);
      }
    }
}

<p>再访问一下我们的/home/index</p>
<p><div align="center"></div></p>
<p>可以看出已经实现了我们具体的实现类注入</p>
<p>到了这里,大家可能会发现在我们的Global.asax文件中,全部仓储层、服务层的类都必要一个个注册,那如果很多类的话,那可就写老长了,那有什么解决方法吗,答案是肯定的。Autofac还提供了其他方式注入,下面就介绍下我们的解决办法。</p>
<p>还记得我们上面介绍仓储层和服务层实现类的时候都实现了我们的IDependency接口吗,接下来就派上大用场了,我们可以一次性注册全部实现了IDependency接口的类。</p>
<p>修改一下我们的依赖注入方式来实现一次性注入:</p>

using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Reflection;
using System.IO;

using Autofac;
using Autofac.Integration.Mvc;
using TianYa.DotNetShare.Model;

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控制器(注册全部到控制器,控制器注入,就是必要在控制器的构造函数中接收对象)
            //PropertiesAutowired:答应属性注入
            builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();

            //一次性注册全部实现了IDependency接口的类
            Type baseType = typeof(IDependency);
            Assembly[] assemblies =
                Directory.GetFiles(AppDomain.CurrentDomain.RelativeSearchPath, "*.dll").Select(Assembly.LoadFrom).ToArray();
            builder.RegisterAssemblyTypes(assemblies)
                   .Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract)
                   .AsSelf().AsImplementedInterfaces()
                   .PropertiesAutowired().InstancePerLifetimeScope();

            //设置依赖解析器
            var container = builder.Build();
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
      }
    }
}

<p>最后再来看下我们的控制器</p>

using System.Web.Mvc;
using TianYa.DotNetShare.Service;
using TianYa.DotNetShare.Repository;
using TianYa.DotNetShare.Repository.Impl;

namespace TianYa.DotNetShare.MvcDemo.Controllers
{
    public class HomeController : Controller
    {
      /// <summary>
      /// 定义仓储层学生实现类对象
      /// </summary>
      public StudentRepository StuRepositoryImpl { get; set; }

      /// <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 stu1 = StuRepository.GetStuInfo("10000");
            var stu2 = StuService.GetStuInfo("10001");
            var stu3 = StuRepositoryImpl.GetStuInfo("10002");
            string msg = $"学号:10000,姓名:{stu1.Name},性别:{stu1.Sex},年龄:{stu1.Age}<br />";
            msg += $"学号:10001,姓名:{stu2.Name},性别:{stu2.Sex},年龄:{stu2.Age}<br />";
            msg += $"学号:10002,姓名:{stu3.Name},性别:{stu3.Sex},年龄:{stu3.Age}";
            return Content(msg);
      }
    }
}

<p>再次访问一下我们的/home/index</p>
<p><div align="center"></div></p>
<p>可以看出成功返回了学生信息,说明我们的一次性注入成功了。</p>
<p>至此我们就介绍完了Autofac依赖注入在MVC中的具体使用,下一章我们将继续简单的介绍下Autofac在普通的WebForm当中的使用。</p>
<p>demo源码:</p>

链接:https://pan.baidu.com/s/1jUbf1pk2-bSybf9OfUh8Tw
提取码:24ki

<p> </p>
<p>参考博文:https://www.cnblogs.com/fei686868/p/10979790.html<br /></p>
<p><strong>版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!</strong></p><br>来源:<a href="https://www.cnblogs.com/xyh9039/p/11318247.html" target="_blank">https://www.cnblogs.com/xyh9039/p/11318247.html</a><br>免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: ASP.NET MVC IOC依赖注入之Autofac系列(一)- MVC当中应用