ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

IOC容器--Autofac及使用

2022-05-14 10:33:13  阅读:193  来源: 互联网

标签:Autofac AutoFacDemo -- AutoFac userService using IOC public


IOC容器 Autofac及使用

 

符合IOC理念的容器可以自己进行设计,也可以使用第三方的,有的还提供了源码。Autofac是一种IOC的实现,这篇文章对Autofac做个简单的介绍,详细的可以看下Autofac官方文档。先简单说一下IOC和DI:

  • IOC:是一种设计理念,调用者不再创建类的实例对象,而是交给容器,这就是控制反转
  • DI:容器创建好类对象再注入调用者的过程,就是依赖注入,通常有属性注入、构造函数注入等方式

总结来说,IoC是一种很宽泛的理念,DI是实现了IoC的其中一种方法。

一、Autofac在.Net Framework中使用

接下来将通过一个demo来使用Autofac,构建MVC应用

1、先构建AutoFac.Modle

复制代码
using System;
namespace AutoFacDemo.AutoFac.Modle
{
    public class UserModel
    {
        public string Id { get; set; }
        public string UserName { get; set; }
    }
}
复制代码

2、构建AutoFac.IRepository和AutoFac.Repository

复制代码
using AutoFacDemo.AutoFac.Modle;

namespace AutoFacDemo.AutoFac.IRepository
{
    public interface IUserRepository
    {
        void AddUser(UserModel userModel);
    }
}
复制代码 复制代码
using AutoFacDemo.AutoFac.IRepository;
using AutoFacDemo.AutoFac.Modle;

namespace AutoFacDemo.AutoFac.Repository
{
    public class UserRepository:IUserRepository
    {
        public void AddUser(UserModel userModel)
        {
           //do something
        }
    }
}
复制代码

3、构建AutoFac.IService和AutoFac.Service

复制代码
using AutoFacDemo.AutoFac.Modle;

namespace AutoFacDemo.AutoFac.IService
{
    public interface IUserService
    {
        void Add(UserModel userModel);
    }
}
复制代码 复制代码
using AutoFacDemo.AutoFac.IRepository;
using AutoFacDemo.AutoFac.IService;
using AutoFacDemo.AutoFac.Modle;

namespace AutoFacDemo.AutoFac.Service
{
    public class UserService : IUserService
    {
        private IUserRepository _userRepository;
        /// <summary>
        /// 通过构造函数注入
        /// </summary>
        /// <param name="userRepository"></param>
        public UserService(IUserRepository userRepository)
        {
            this._userRepository = userRepository;
        }
        public void Add(UserModel userModel)
        {
            this._userRepository.AddUser(userModel);
        }
    }
}
复制代码

4、把Autofac的引用添加进项目

 

5、初始化AutoFac

在应用启动的地方, 初始化AutoFac,需要添加一个 ContainerBuilder 并且通过它注册你的组件 ,组件可以是一个表达式, .NET 类型, 或者其他暴露一个或多个服务 的一段代码, 同时它也可以引入其他的依赖。我们在Global中初始化AutoFac,对于上边的MVC示例应用, 我们需要注册所有的组件 (类) 并且暴露他们的服务 (接口) , 这样对象就能很好地连接起来.

复制代码
using Autofac;
using AutoFacDemo.AutoFac.IRepository;
using AutoFacDemo.AutoFac.IService;
using AutoFacDemo.AutoFac.Repository;
using AutoFacDemo.AutoFac.Service;
using System.Web.Mvc;
using System.Web.Routing;

namespace AutoFacDemo
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            InitAutofac();
        }
        /// <summary>
        /// 初始化Autofac
        /// </summary>
        public void InitAutofac()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<UserRepository>().As<IUserRepository>();
            builder.RegisterType<UserService>().As<IUserService>();
            AutofacHelper.Container = builder.Build();
        }
    }
}
复制代码

同时我们还要保存这个容器Container ,这样就可以在后续解析类型,所以定义一个公共类专门保存该容器:

复制代码
using Autofac;

namespace AutoFacDemo
{
    public class AutofacHelper
    {
        public static IContainer Container { get; set; }

        public static T GetService<T>()
        {
            return (T)Container?.Resolve(typeof(T));
        }
    }
}
复制代码

现在我们已经拥有了一个注册了所有组件的容器 , 并且他们暴露了合适的服务,接下来就是使用它们

6、应用执行

注意:在应用程序执行阶段,你需要充分利用这些刚注册的组件。 你可以从一个生命周期中解析它们。容器本身是也是一个生命周期,从技术角度来说,你可以直接从Container解析组件,然而, 我们并不推荐直接这么做。解析组件时,根据定义的实例作用域, 创建一个对象的新实例 (解析一个组件大致相当于调用"new"实例化一个类)。 一些组件需要被释放 (实现IDisposable接口) ,Autofac会为你在生命周期释放时处理组件的释放。然而, 容器在应用的生命周期内一直存在,如果你直接从该容器中解析了太多东西,应用结束时将会有一堆东西等着被释放,这是非常不合适的 (很有可能造成"内存泄漏")。因此, 我们可以从容器中创建一个子生命周期 并从中解析。当你完成了解析组件, 释放掉子生命周期, 其他所有也就随之被一并清理干净了。(当使用Autofac 集成类库时, 大部分情况下子生命周期创建已经完成了, 因此无需考虑.)

对于上边的MVC示例应用程序,我们在生命周期内实现"AddUser"方法并在结束调用后释放它。

复制代码
using AutoFacDemo.AutoFac.IService;
using AutoFacDemo.AutoFac.Modle;
using System.Web.Mvc;

namespace AutoFacDemo.Controllers
{
    public class HomeController : Controller
    {
        private readonly IUserService _userService;
        /// <summary>
        /// 构造函数注入
        /// </summary>
        /// <param name="userService"></param>
        public HomeController(IUserService userService)
        {
            _userService = userService;
        }

        // GET: Home
        public ActionResult Index()
        {
            UserModel userModel = new UserModel
            {
                Id = "521",
                UserName = "dachongzi"
            };
            _userService.Add(userModel);
            return View();
        }
    }
}
复制代码

这样整个流程就走完了,但是运行起来HomeController中会报错“没有为该对象定义无参数的构造函数“,如果我们加上一个无参构造函数,程序就不走有参数的构造函数了,也就无法注入了,这是什么原因呢???主要是我们在Global中的InitAutofac()方法中没有注册MVC相关的组件,比如builder.RegisterControllers。这里就不详细介绍这块了,可以看文档。我们采用另一种方式注入:我们采用属性注入的方式:

复制代码
using AutoFacDemo.AutoFac.IService;
using AutoFacDemo.AutoFac.Modle;
using System.Web.Mvc;

namespace AutoFacDemo.Controllers
{
    public class HomeController : Controller
    {
        //IUserService _userService { get; }= AutofacHelper.GetService<IUserService>();      
        private readonly IUserService _userService = AutofacHelper.GetService<IUserService>();
        
        public ActionResult Index()
        {
            UserModel userModel = new UserModel
            {
                Id = "521",
                UserName = "dachongzi"
            };
            _userService.Add(userModel);
            return View();
        }
    }
}
复制代码

 完美解决。

二、Autofac在.Net Core中使用

1、.netcore默认的依赖注入

.netcore有默认的依赖注入,先来看下默认的依赖注入实现。构建一个.netcore mvc项目。项目结构如下:

 先来定义一个接口及相应实现:

复制代码
namespace AutofacDemo.Models
{
    /// <summary>
    /// 用户model
    /// </summary>
    public class UserModel
    {
        public string Id { get; set; }
        public string UserName { get; set; }
    }
    /// <summary>
    /// 接口
    /// </summary>

    public interface IUserService
    {
        void Add(UserModel userModel);
    }
    /// <summary>
    /// 接口实现
    /// </summary>
    public class UserService : IUserService
    {
        public void Add(UserModel userModel)
        {
            //do something
        }
    }
}
复制代码

startup.cs中默认的依赖注入:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddScoped<IUserService, UserService>();
}

然后在控制器使用:

复制代码
public class HomeController : Controller
{
        private readonly ILogger<HomeController> _logger;
        private IUserService _userService;
        public HomeController(ILogger<HomeController> logger, IUserService userService)
        {
            _logger = logger;
            _userService = userService;
        }

        public IActionResult Index()
        {
            _userService.Add(new UserModel { Id = "123123", UserName = "test" });
            return View();
        }
}
复制代码

除了.netcore默认使用的依赖注入外,还可以使用第三方的依赖注入插件实现。

2、Autofac实现依赖注入

在进行实例演示之前先来看下Autofac中涉及的生命周期:

复制代码
//1、瞬时生命周期:注册之后,每次获取到的服务实例都不一样(默认的注册方式)
containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerDependency();
//2、单例生命周期:整个容器中获取的服务实例都是同一个
containerBuilder.RegisterType<UserService>().As<IUserService>().SingleInstance();
//3、作用域生命周期:在相同作用域下获取到的服务实例是相同的
containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope();
//4、作用域生命周期:可以指定到某一个作用域,然后在相同作用域下共享服务实例
containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerMatchingLifetimeScope("My");
//5、http请求上下文的生命周期:在一次Http请求上下文中,共享一个组件实例。仅适用于asp.net mvc开发。
containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerRequest();
//6、拥有隐式关系类型的创建新的嵌套生命周期的作用域,在一个生命周期域中所拥有的实例创建的生命周期中,
//  每一个依赖组件或调用Resolve()方法创建一个单一的共享的实例,并且子生命周期域共享父生命周期域中的实例
containerBuilder.RegisterType<UserService>().InstancePerOwned<IUserService>();
复制代码

(1)方式一:单个注入

构建一个.netcore mvc项目。项目结构如下:

  • 引入包:

  

  • program.cs代码:
复制代码
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace AutofacDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                }).UseServiceProviderFactory(new AutofacServiceProviderFactory());//使用autofac的容器工厂替换系统默认的容器
    }
}
复制代码
  • 然后在Startup中增加一个方法ConfigureContainer:
复制代码
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
     //指定服务的注册
     containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope().AsImplementedInterfaces();
}
复制代码
  • 然后在控制器中使用:
复制代码
public class HomeController : Controller
{
        private readonly ILogger<HomeController> _logger;
        private IUserService _userService;
        public HomeController(ILogger<HomeController> logger, IUserService userService)
        {
            _logger = logger;
            _userService = userService;
        }

        public IActionResult Index()
        {
            _userService.Add(new UserModel { Id = "123123", UserName = "test" });
            return View();
        }
}
复制代码

(2)方式二:批量注入

构建项目,项目结构如下:

  • 类库AutofacBatchDemo.IService中 IUserService.cs文件代码如下:
复制代码
namespace AutofacBatchDemo.IService
{
    /// <summary>
    /// 用户model
    /// </summary>
    public class UserModel
    {
        public string Id { get; set; }
        public string UserName { get; set; }
    }
    /// <summary>
    /// 接口
    /// </summary>

    public interface IUserService
    {
        void Add(UserModel userModel);
    }
}
复制代码
  • 类库AutofacBatchDemo.Service中UserService.cs代码如下:
复制代码
using AutofacBatchDemo.IService;

namespace AutofacBatchDemo.Service
{
    /// <summary>
    /// 接口实现
    /// </summary>
    public class UserService : IUserService
    {
        public void Add(UserModel userModel)
        {
            //do something
        }
    }
}
复制代码
  • AutofacBatchDemo项目中program.cs代码如下:
复制代码
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace AutofacBatchDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                }).UseServiceProviderFactory(new AutofacServiceProviderFactory());//使用autofac的容器工厂替换系统默认的容器
    }
}
复制代码
  • AutofacBatchDemo项目中Startup.cs代码如下:
复制代码
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
     Assembly service = Assembly.Load("AutofacBatchDemo.Service");
     Assembly iservice = Assembly.Load("AutofacBatchDemo.IService");

     containerBuilder.RegisterAssemblyTypes(service, iservice)
          .Where(t => t.FullName.Contains("Service") && !t.IsAbstract) //类名包含service命名,且类型不能是抽象的
          .InstancePerLifetimeScope() //生命周期
          .AsImplementedInterfaces()
          .PropertiesAutowired(); //属性注入
}
复制代码
  • 然后在控制器中使用:
复制代码
using System.Diagnostics;

namespace AutofacBatchDemo.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private IUserService _userService;
        public HomeController(ILogger<HomeController> logger, IUserService userService)
        {
            _logger = logger;
            _userService = userService;
        }

        public IActionResult Index()
        {
            _userService.Add(new UserModel { Id = "sdfa", UserName = "dfasfd" });
            return View();
        }
}
复制代码

(3)一个接口多个实现的服务注入

在实际应用中有这样一种场景,比如IUserService接口被多个类继承并实现,那么此时应该如何注册服务并实现调用呢?

复制代码
public class HomeController : Controller
{
        private readonly ILogger<HomeController> _logger;
        private readonly IEnumerable<IUserService> _userServices = null;
        public HomeController(ILogger<HomeController> logger, IEnumerable<IUserService> userServices)
        {
            _logger = logger;
            _userServices = userServices;
        }

        public IActionResult Index()
        {
            int i = 0;
            foreach (var item in _userServices)
            {
                i++;
                item.Add(new UserModel { Id="i",UserName="阿斯顿发"});
            }
            return View();
        }
}
复制代码

(4)封装Autofac的注入

为了简化Startup中的代码,我们将startup.cs中的相关代码提取出来,放到单独的文件中。要新建一个AutofacModule类,并继承Autofac.Module,同时重写其中的Load方法,具体代码如下:

复制代码
using Autofac;
using System.Linq;
using System.Reflection;

namespace AutofacBatchDemo.Models
{
    public class AutofacModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            //反射程序集方式服务注册
            Assembly service = Assembly.Load("AutofacBatchDemo.Service");
            Assembly iservice = Assembly.Load("AutofacBatchDemo.IService");
            builder.RegisterAssemblyTypes(service, iservice)
            .Where(t => t.FullName.Contains("Service") && !t.IsAbstract) //类名包含service命名,且类型不能是抽象的 
                .InstancePerLifetimeScope() //作用域生命周期
                .AsImplementedInterfaces()
                .PropertiesAutowired(); //属性注入
        }
    }
}
复制代码

重写Load方法中的逻辑其实就是将原本 写在Startup中的注册代码迁移到AutofacModule中,然后将Startup中的ConfigureContainer方法修改成如下:

public void ConfigureContainer(ContainerBuilder containerBuilder)
{
      containerBuilder.RegisterModule<AutofacModule>();
}

三、AutoFac中常用方法说明

虽然AutoFac中的方法都有说明,但是有些常用的方法不太好理解,不晓得这个方法的作用,是干嘛的。

1、AsImplementedInterfaces()  是以接口方式进行注入,注入这些类的所有的公共接口作为服务

2、InstancePerRequest()  每次请求共享同一个实例,使用ef时,使不同的操作使用同一个数据上下文

3、PreserveExistingDefaults() 如果不止一个组件暴露了相同的服务, Autofac将使用最后注册的组件作为服务的提供方,那么使用PreserveExistingDefaults防止后面注册的覆盖前面注册的

builder.Register<A>() .As<IA>();

builder.Register<AA>() .As<IA>();  这样AA就会覆盖A

builder.Register<AA>() .As<IA>().PreserveExistingDefaults()   这样A是IA的默认值,不会被AA覆盖

4、InstancePerDependency为每个依赖或者调用(Resolve())都创建一个新的对象,唯一的实例

5、SingleInstance 为每次请求都使用同一个对象,单例模式.告诉容器,是个单例,但这个单例不用自己实现。

6、RegisterGeneric 对泛型类进行注册

builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>))

7、RegisterInstance() 注册一个实例,比如: 对已经存在的对象的实例进行注册,这样可以使实例转化为由容器托管的实例

8、RegisterControllers() 注册Controllers

builder.RegisterControllers(Assembly.GetExecutingAssembly())  注册当前程序集中的所有Controllers

9、RegisterFilterProvider() 注册Filter过滤器

builder.RegisterFilterProvider()

10、RegisterAssemblyTypes 注册类

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())注册了当前程序集内的所有的类

11、InstancePerMatchingLifetimeScope 在一个做标识的生命周期域中,每一个依赖或调用创建一个单一的共享的实例。打了标识了的生命周期域中的子标识域中可以共享父级域中的实例

builder.RegisterType<A>() .AsImplementedInterfaces() .InstancePerMatchingLifetimeScope();

12、InstancePerHttpRequest在一次Http请求上下文中,共享一个组件实例。仅适用于asp.net mvc开发

13、InstancePerLifetimeScope在一个生命周期中,每一次的依赖组件或调用(Resolve())创建一个单一的共享的实例,且每一个不同的生命周期域,实例是不同的

14、UsingConstructor(参数) 自动装配,手动使用特定的构造函数

builder.RegisterType<A>().UsingConstructor(typeof(IA),typeof(IAA))   这样指定调用的是A(IA,IAA)的构造函数,如果该构造函数不存在则报错

15、[AsClosedTypesOf(open)]AsClosedTypesOf(typeof(IA<>)):如果是多继承的话,注册的程序集所对应的服务(接口)是离这个类最近的开放的泛型实例(接口)

builder.RegisterAssemblyTypes(A).AsClosedTypesOf(typeof(IRepository<>));

四、总结

上边介绍了如何简单的使用AutoFac,但依然有很多你可以做的:

 

标签:Autofac,AutoFacDemo,--,AutoFac,userService,using,IOC,public
来源: https://www.cnblogs.com/ivan0915/p/16269263.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有