.NET 依赖注入DI 注册服务
一、概念
控制反转
IoC/DI容器控制应用主程序。
IoC/DI容器控制对象本身的创建、实例化;控制对象之间的依赖关系。
3、何谓反转(对应于正向)?
因为现在应用程序不能主动去创建对象了,而是被动等待对象容器给它注入它所需要的资源,所以称之为反转。
4、哪些方面反转了?
1.创建对象
2.程序获取资源的方式反了
5、为何需要反转?
1.引入IoC/DI容器过后,体系更为松散,而且管理和维护以及项目升级更有序;
2.类之间真正实现了解耦
依赖
1、什么是依赖(按名称理解、按动词理解)?
依赖(按名称理解):依赖关系;
依赖(按动词理解):依赖的动作
2、谁依赖于谁?
应用程序依赖于IoC/DI容器
3、为什么需要依赖?
因为发生了反转,应用程序依赖的资源都是IoC/DI容器里面
4、依赖什么东西?
应用程序依赖于IoC/DI容器为它注入所需要的资源。(比如:依赖关系)
注入
1、谁注入于谁?
IoC/DI容器注入于应用程序。
2、注入什么东西?
注入应用程序需要的对象,比如依赖关系。
3、为何要注入?
因为程序要正常运行需要访问这些对象
IOC(控制反转Inversion of Control)
控制反转(Inversion of Control)就是使用对象容器反过来控制应用程序所需要的外部资源,这样的一种程序开发思想,调用者不再创建被调用者的实例,由IOC框架实现(容器创建)所以称为控制反转;
创建对象和对象非托管资源的释放都由外部容器去完成,实现项目层与层之间的解耦的一种设计思想。
DI(依赖注入)和DIP(依赖倒置原则)
相信很多人还分不清楚DI和DIP这两个词,甚至认为它们就是同一个词。
1、依赖倒置原则(Dependency Inversion Principle)为我们提供了降低模块间耦合度的一种思路,而依赖注入(Dependency Injection)是一种具体的实施方法,
容器创建好实例后再注入调用者称为依赖注入,就是应用程序依赖IOC容器来注入所需要的外部资源,这样一种程序的开发思想。
2、能做什么(What)?
松散耦合对象,解耦项目架构层。
3、怎么做(How)?
使用Autofac/Unity/Spring等框架类库,里面有实现好了的IoC/DI容器。
4、用在什么地方(Where)?
凡是程序里面需要使用外部资源的情况,比如创建对象,都可以考虑使用IoC/DI容器。
DI和IOC是同一概念吗?
肯定不是同一概念啊,但它们两个描述的是同一件事件,从不同的角度来说:IOC是从对象容器的角度;DI是从应用程序的角度。
1、控制反转的描述:对象容器反过来控制应用程序,控制应用程序锁所需要的一些对象,比如DbContext。
2、依赖注入的描述:应用程序依赖对象容器,依赖它注入所需要的外部资源。
对IoC的理解:
1、应用程序无需主动new对象,而是描述对象应该如何被创建(构造方法、属性、方法参数等)。
2、应用程序不需要主动装配对象之间的依赖关系,而是描述需要哪个服务,IoC容器会帮你装配,被动接受装配。
3、主动变被动,是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。
白话解释:
为啥使用依赖注入
private ILoginService<ApplicationUser> _loginService;
public AccountController()
{
    //new
  _loginService = new EFLoginService()
}public AccountController(ILoginService<ApplicationUser> loginService)
{
  _loginService = loginService;
}对象生命周期: IServiceScope
- AddTransient(瞬态):每次调用都创建一个新的对象,即使同一个请求获取多次也会是不同的实例
- AddScoped(范围); 一个范围(using)内拿到同一个对象 类有状态且有Scope控制,每次请求,都获取一个新的实例。同一个请求获取多次会得到相同的实例
- AddSingleton(单例) : 同一个对象 类无状态(没有属性,没有成员变量) 第一个请求上创建一个新实例,并且在应用程序的剩余生命周期中,将相同的实例提供给所有使用者类。
- 范围服务通常应由单个Web请求/线程使用。因此,不应该在线程之间共享服务范围。
- 配置为单例的服务可能会导致应用程序中的内存泄漏。
- 内存泄漏通常是由单例服务引起的。这是因为创建的实例不会被丢弃,它将保留在内存中直到应用程序结束。因此,一旦不使用它们,最好将它们释放。
- 将服务注册为临时服务会缩短其使用寿命,通常可能不太在乎多线程和内存泄漏。
- 不要在单例服务中依赖瞬态或作用域服务。因为瞬时服务在单例服务注入时成为一个单例实例,并且如果瞬态服务不旨在支持这种情况,则可能导致问题。在这种情况下,ASP.NET Core的默认DI容器已经引发异常。
二、使用DI
           //注册
            ServiceCollection services = new ServiceCollection();
            //services.AddTransient<TestServiceImp1>();//瞬态
            //services.AddSingleton<TestServiceImp1>();//单例
            services.AddScoped<TestServiceImp1>();//范围
            //建立  ServiceProvider 服务定位器
            using (ServiceProvider sp = services.BuildServiceProvider())
            {
                ////使用  要个一个对象
                //TestServiceImp1 t = sp.GetService<TestServiceImp1>();
                //t.Name = "lily";
                //t.say();
                //TestServiceImp1 t1 = sp.GetService<TestServiceImp1>();
                //Console.WriteLine(object.ReferenceEquals(t, t1));
                //t1.Name = "tom";
                //t1.say();
                //t.say();//1、瞬态  lily   新的对象  2、单例  tom  同一对象
                TestServiceImp1 tt1;
                //using 定义了Scoped 范围
                using (IServiceScope scope1 = sp.CreateScope())
                {
                    //在scope中获取Scope相关的对象,scope1.ServiceProvider而不是sp
                    TestServiceImp1 t = scope1.ServiceProvider.GetService<TestServiceImp1>();
                    t.Name = "lily";
                    t.say();
                    TestServiceImp1 t1 = scope1.ServiceProvider.GetService<TestServiceImp1>();
                    Console.WriteLine(object.ReferenceEquals(t, t1));//ture
                    tt1 = t;
                }
                using (IServiceScope scope2 = sp.CreateScope())
                {
                    //在scope中获取Scope相关的对象,scope1.ServiceProvider而不是sp
                    TestServiceImp1 t = scope2.ServiceProvider.GetService<TestServiceImp1>();
                    t.Name = "lily";
                    t.say();
                    TestServiceImp1 t1 = scope2.ServiceProvider.GetService<TestServiceImp1>();
                    Console.WriteLine(object.ReferenceEquals(t, t1));//ture
                    Console.WriteLine(object.ReferenceEquals(tt1, t1));//flase 一个范围(using)内拿到同一个对象
                }
            }
            //ITestService t = new TestServiceImp1();
            //t.Name = "xw";
            //t.say();
            Console.Read(); ServiceCollection services = new ServiceCollection();
            //ITestService 服务类型(接口) ,TestServiceImp1 实现类型
            services.AddScoped<ITestService, TestServiceImp1>();
            //services.AddScoped(typeof(ITestService), typeof(TestServiceImp1));
            using (ServiceProvider sp = services.BuildServiceProvider())
            {
                //获得服务  注册什么类型拿什么类型
                //GetService 如果找不到服务,就返回null
                //ITestService ts1 = sp.GetService<ITestService>();
                //ITestService ts1= (ITestService)sp.GetService(typeof(ITestService));//非泛型方法
                //ts1.Name = "tom";
                //ts1.say();
                //Console.WriteLine(ts1.GetType());
                // GetServices  s获取全部服务
                var tests = sp.GetServices<ITestService>();
                foreach (var t in tests)
                {
                    Console.WriteLine(t.GetType());
                }
            }
            Console.ReadLine(); services.AddTransient<MQ.ISOService, MQ.SOService>();//这个<>其实就是,当代码读取到MQ.ISOService的接口的时候,会返回MQ.SOService这个对象。- 如果要修改B 或者替换B 就必须修改A
- .。。。
public interface IMyDependency
{
    void WriteMessage(string message);
}public class MyDependency : IMyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
//注册
builder.Services.AddScoped<IMyDependency, MyDependency>();
var app = builder.Build();public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;
    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }
    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;
    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }
    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddScoped<IMyDependency, MyDependency2>();
var app = builder.Build();- 不使用具体类型 MyDependency,仅使用它实现的 IMyDependency 接口。 这样可以轻松地更改实现,而无需修改控制器或 Razor 页面。
- 不创建 MyDependency 的实例,这由 DI 容器创建。
后期补:
1、MVC 控制器中可以
        public IActionResult About([FromServices] IDateTime dateTime)
        {
            return Content($"Current server time: {dateTime.Now}");
        }2、配置文件注入
public class SampleWebSettings
{
    public string Title { get; set; }
    public int Updates { get; set; }
}public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDateTime, SystemDateTime>();
    services.Configure<SampleWebSettings>(Configuration);
    services.AddControllersWithViews();
}public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddJsonFile("samplewebsettings.json",
                    optional: false,
                    reloadOnChange: true);
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}{
  "Title": "Title11",
  "Updates": 11
}
以下代码从服务容器请求 IOptions<SampleWebSettings> 设置,并通过 Index 方法使用它们:
public class SettingsController : Controller
{
    private readonly SampleWebSettings _settings;
    public SettingsController(IOptions<SampleWebSettings> settingsOptions)
    {
        _settings = settingsOptions.Value;
    }
    public IActionResult Index()
    {
        ViewData["Title"] = _settings.Title;
        ViewData["Updates"] = _settings.Updates;
        return View();
    }
}<h1>@ViewData["Title"] --Title</h1>
<h1>@ViewData["Updates"] --Updates</h1>三、原理和源码分析
扩展方法-服务构建器 (动态,可配置切换服务)
            // 自定义服务类
            //第一种
            //services.AddTransient<IMessageService, EmailService>();
            //services.AddTransient<IMessageService, SmsService>();
            
            //第二种 封装服务注册的方法(服务构建器) 可配置选择
            services.AddMessage(options=>options.UseEmail());    //扩展类 扩展Startup里面 ConfigureServices 的 IServiceCollection
    public static class MessageServericeExtensions
    {
        public static void AddMessage(this IServiceCollection services)
        {
            services.AddSingleton<IMessageService, EmailService>();
        }
        public static void AddMessage(this IServiceCollection services,Action<MessageServiceBuider> options)
        {
            //创建构建器对象
            var builder =new MessageServiceBuider(services);
            // 调用委托,对象传过来
            options(builder);
        }
    }    //消息服务构建器
    //配置类 
    public class MessageServiceBuider
    {
        public IServiceCollection _services { get; set; }
        public MessageServiceBuider(IServiceCollection services)
        {
            this._services = services;
        }
        public void UseEmail()
        {
            _services.AddSingleton<IMessageService, EmailService>();
        }
        public void UseSms()
        {
            _services.AddSingleton<IMessageService, SmsService>();
        }
    } 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号