.NET Core 依赖注入 Microsoft.Extensions.DependencyInjection

前言

依赖注入是一个非常重要的编程思想,就和面向过程和面向对象一样,IOC和控制反转是一种解耦的编程思想。

什么是依赖注入

[C#]理解和入门依赖注入

为什么要用IOC:inversion of controll反转控制(把创建对象的权利交给框架)

C# 使用依赖注入

框架介绍

目前.NET 有两个最优的依赖注入框架

  • Microsoft.Extensions.DependencyInjection:微软官方依赖注入框架,听说在.net core 8.0得到了最强的性能提升
  • Autofac:听说也是最强的依赖注入框架,性能强,开销低,功能完善。

Dependency injection in ASP.NET Core

Autofac 官网

深入浅出依赖注入容器——Autofac

Microsoft.Extensions.DependencyInjection

目前打算用微软的IOC,毕竟是官方背书,性能有保证。

C# 依赖注入IServiceCollection的AddSingleton方法使用

Nuget安装

在这里插入图片描述

简单单例使用

声明个测试类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NETCore8.Models
{
    public class Person
    {
        public int Id { get; set; }

        public string ?Name { get; set; }

        public int Age { get; set; }


    }
}

主函数代码

using Microsoft.Extensions.DependencyInjection;
using NETCore8.Models;
using Newtonsoft.Json;
using System.ComponentModel.Design;

namespace NETCore8
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //构造依赖注入容器
            IServiceCollection services = new ServiceCollection();
            //注入Person单例,生命周期暂时不展开
            services.AddSingleton<Person>();
            var builder = services.BuildServiceProvider();

            //初始化单例
            var res = builder.GetService<Person>();
            res.Name = "小刘";
            res.Age = 15;
            Console.WriteLine(JsonConvert.SerializeObject(res));

            //从容器中拿到Person单例,确认是否已被赋值为小刘
            var res2 = builder.GetService<Person>();
            Console.WriteLine(JsonConvert.SerializeObject(res2));
            //修改单例,查看容器中的单例是否被修改
            res2.Name = "小红";
            res2.Age = 23;


            //再从容器中拿出单例
            var res3 = builder.GetService<Person>();
            Console.WriteLine(JsonConvert.SerializeObject(res3));
            

            Console.WriteLine("Hello, World!");
            Console.ReadKey();
        }
    }
}


打印结果

在这里插入图片描述
这个说明这个单例一旦被修改了,容器中的数据就会被修改。但是这样仅仅是和全局静态的效果一样。依赖注入没有这么简单

自动装配

自动装配的意思就是自动依赖注入。就是你不需要主动去声明构造函数,IOC容器会自动帮你去使用构造函数。

举例

这里为了简单说明,这里只使用单例自动装配举例。

namespace IOC_Test.Models
{
    public class Person
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public int Age {  get; set; }   
    }
}

namespace IOC_Test.Services
{
    public class PersonService
    {
        public Person Person { get; set; }
        /// <summary>
        /// 无参构造函数
        /// </summary>
        public PersonService() {
            Person = new Person();
        }


        /// <summary>
        /// 有参构造函数,IOC是选择尽可能多的参数构造
        /// </summary>
        /// <param name="person"></param>
        public PersonService(Person person)
        {

            this.Person = person;
        }
    }
}

自动装配测试用例

using IOC_Test.Models;
using IOC_Test.Services;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;

namespace IOC_Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();
            //注入依赖
            services.AddSingleton<Person>();
            services.AddSingleton<PersonService>();
            //生成IOC容器
            var builder = services.BuildServiceProvider();

            //两次打印,第一次打印PersonService的Person
            {
                var res = builder.GetService<PersonService>();
                Console.WriteLine(JsonConvert.SerializeObject(res?.Person));
            }
            //修改Person,看看PersonService里面是不是会受影响
            {
                var res = builder.GetService<Person>();
                res.Name = "小王";
                res.Age = 10;
            }
            //再次打印,如果被修改,那么就说明是自动装配。如果没被修改,就说明没有将Person自动注入到PersonService
            {
                var res = builder.GetService<PersonService>();
                Console.WriteLine(JsonConvert.SerializeObject(res?.Person));
            }


            Console.WriteLine("Hello, World!");

            Console.ReadLine();
        }
    }
}

打印结果

在这里插入图片描述

自动装配执行顺序

测试用例

这里我们新建一个Dog

namespace IOC_Test.Models
{
    public class Dog
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }
    }
}

Person

namespace IOC_Test.Models
{
    public class Person
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public int Age {  get; set; }   
    }
}

PersonService

namespace IOC_Test.Services
{
    public class PersonService
    {
        public Person Person { get; set; }

        public Dog Dog { get; set; }

        /// <summary>
        /// 无参构造函数
        /// </summary>
        public PersonService() {
            Person = new Person();
        }
    }
}

主函数

namespace IOC_Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();
            //注入依赖
            services.AddSingleton<Person>();
            services.AddSingleton<PersonService>();
            services.AddSingleton<Dog>();
            //生成IOC容器
            var builder = services.BuildServiceProvider();

            //两次打印,第一次打印PersonService
            {
                var res = builder.GetService<PersonService>();
                Console.WriteLine(JsonConvert.SerializeObject(res));
            }
            //修改Person和Dog,看看PersonService里面是不是会受影响
            {
                var person = builder.GetService<Person>();
                person.Name = "小王";
                person.Age = 10;
                var dog = builder.GetService<Dog>();
                dog.Name = "旺财";
                dog.Age = 2;
            }
            //再次打印,查看自动装配如何执行
            {
                var res = builder.GetService<PersonService>();
                Console.WriteLine(JsonConvert.SerializeObject(res));
            }


            Console.WriteLine("Hello, World!");

            Console.ReadLine();
        }
    }
}

有歧义构造函数
namespace IOC_Test.Services
{
    public class PersonService
    {
        public Person Person { get; set; }

        public Dog Dog { get; set; }

        /// <summary>
        /// 无参构造函数
        /// </summary>
        public PersonService() {
            Person = new Person();
        }

        public PersonService(Person person)
        {
            this.Person = person;
        }

        public PersonService(Dog dog) {
            this.Dog = dog;
        }
    }
}

如果构造函数出现歧义,比如这里既可以选择Person构造,又可以选择Dog构造,会报错
在这里插入图片描述

渐进式构造函数
namespace IOC_Test.Services
{
    public class PersonService
    {
        public Person Person { get; set; }

        public Dog Dog { get; set; }

        /// <summary>
        /// 无参构造函数
        /// </summary>
        public PersonService() {
            Person = new Person();
        }

        public PersonService(Person person)
        {
            this.Person = person;
        }

        public PersonService(Person person,Dog dog) {
            this.Person= person;
            this.Dog = dog;
        }
    }
}

运行成功
在这里插入图片描述

循环依赖

Person注入Dog,Dog注入Person,看看效果如何

namespace IOC_Test.Models
{
    public class Person
    {
        public Dog Dog { get; set; }

        public int Id { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }
        public Person(Dog dog)
        {
            Dog = dog;
        }
    }
}
namespace IOC_Test.Models
{
    public class Dog
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

        public Person Person { get; set; }
        public Dog(Person person)
        {
            Person = person;
        }
    }
}

在这里插入图片描述

自动装配结论

自动装配是尽可能主动去装配服务,如果出现装配歧义,循环依赖,那么就会主动抛出异常。自动装配可以极大的减少对构造函数维护,我们不需要知道服务是怎么声明的,IOC容器会帮助我们自动声明相互之间的依赖。这张图就能很好的解释自动装配的效果

在这里插入图片描述

手动装配

自动装配是由IOC容器自动装配的类。如果需要装配多个同类的服务,那就要手动进行区别了。

手动注入

internal class Program
{
    static void Main(string[] args)
    {
        IServiceCollection services = new ServiceCollection();
        services.AddSingleton<Person>(sp =>
        {
            var res = new Person() {
                Name = "小红",
                Age = 19
            };
            return res;
        });
        //生成容器
        var builder = services.BuildServiceProvider();
        {
            var res = builder.GetService<Person>();
            Console.WriteLine(JsonConvert.SerializeObject(res));
        }
        

        Console.WriteLine("Hello, World!");

        Console.ReadLine();
    }
}

在这里插入图片描述

别名注入



namespace IOC_Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();
            services.AddKeyedSingleton<Person>("A",(sp,key) =>
            {
                var res = new Person() {
                    Name = "小红",
                    Age = 19
                };
                return res;
            });

            services.AddKeyedSingleton<Person>("B", (sp, key) =>
            {
                var res = new Person()
                {
                    Name = "小蓝",
                    Age = 23
                };
                return res;
            });
            //生成容器
            var builder = services.BuildServiceProvider();
            
            //获取服务,当Key找不到时自动返回Null
            {
                var res = builder.GetService<Person>();
                Console.WriteLine("获取默认服务");
                Console.WriteLine(JsonConvert.SerializeObject(res));
            }
            {
                var res = builder.GetKeyedService<Person>("A");
                Console.WriteLine("获取A,Person");
                Console.WriteLine(JsonConvert.SerializeObject(res));
            }
            {
                var res = builder.GetKeyedService<Person>("B");
                Console.WriteLine("获取B,Person");
                Console.WriteLine(JsonConvert.SerializeObject(res));
            }


            Console.WriteLine("Hello, World!");

            Console.ReadLine();
        }
    }
}

在这里插入图片描述

声明别名的服务将不会自动装配,即使声明的别名相同。建议使用多个不同名的服务来自动装配。手动声明别名需要手动装配对应关系

也可以在输入的时候主动拿到按照Key去寻找服务。

internal class Program
{
    static void Main(string[] args)
    {
        IServiceCollection services = new ServiceCollection();
		//依赖注入是使用的时候去构造,所以声明顺序不影响实际运行顺序,有点类似于回调函数
        services.AddKeyedSingleton<Person>("A",(sp,key) =>
        {
            //Console.WriteLine(key);
            var res = new Person() {
                Name = "小红",
                Age = 19
            };
            return res;
        });
        services.AddKeyedSingleton<PersonService>("A", (sp, key) =>
        {
            return new PersonService(sp.GetKeyedService<Person>(key));
        });

        //生成容器
        var builder = services.BuildServiceProvider();
   
        //获取服务
        {
            var res = builder.GetKeyedService<Person>("A");
            Console.WriteLine("获取默认服务");
            Console.WriteLine(JsonConvert.SerializeObject(res));
        }
        //获取服务
        {
            var res = builder.GetKeyedService<PersonService>("A");
            Console.WriteLine("获取默认服务");
            Console.WriteLine(JsonConvert.SerializeObject(res));
        }

        Console.WriteLine("Hello, World!");

        Console.ReadLine();
    }
}

依赖注入的构造顺序

依赖注入是使用的时候去生成,而不是注入的时候生成

namespace IOC_Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();

            services.AddKeyedSingleton<Person>("A",(sp,key) =>
            {
                Console.WriteLine($"构造函数执行,key[{key}]");
                var res = new Person() {
                    Name = "小红",
                    Age = 19
                };
                return res;
            });
            //生成容器
            var builder = services.BuildServiceProvider();
       
            //获取服务
            {
                Console.WriteLine("获取Key[A]服务");
                var res = builder.GetKeyedService<Person>("A");
                Console.WriteLine(JsonConvert.SerializeObject(res));
            }

            Console.WriteLine("Hello, World!");

            Console.ReadLine();
        }
    }
}

在这里插入图片描述

结尾

IOC容器还有许多别的功能,比如别名,接口注入,注解注入,声明周期等。这个我还不太了解。现在的单例自动装配已经基本满足了我的功能,我以后有时间会去深入了解。

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
WPF (Windows Presentation Foundation) 是一个基于 .NET Framework 的 UI 框架,它提供了丰富的界面设计和开发功能。在 WPF 中使用 Microsoft.Extensions.DependencyInjection 可以帮助我们更好地实现依赖注入(Dependency Injection)。 Microsoft.Extensions.DependencyInjection.NET Core 中的一个轻量级依赖注入框架,它提供了简单、灵活的依赖注入方式。在 WPF 中,我们同样可以使用该框架来实现依赖注入。 首先,我们需要在 WPF 应用程序中安装 Microsoft.Extensions.DependencyInjection NuGet 包。 在 App.xaml.cs 文件中,我们可以创建一个 IServiceCollection 对象,并在其中注册依赖: ```csharp public partial class App : Application { public static IServiceProvider ServiceProvider { get; private set; } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var services = new ServiceCollection(); // 注册依赖 services.AddSingleton<IMyService, MyService>(); // 创建容器 ServiceProvider = services.BuildServiceProvider(); } } ``` 在上面的代码中,我们注册了一个名为 `MyService` 的服务,并指定其实现接口为 `IMyService`。当我们需要使用该服务的时候,可以通过 ServiceProvider 来解析: ```csharp var service = App.ServiceProvider.GetService<IMyService>(); ``` 这样就完成了依赖注入的过程。在 WPF 中,我们通常会在 ViewModel 中使用依赖注入,来实现视图和逻辑的分离。通过使用 Microsoft.Extensions.DependencyInjection,我们可以更方便地管理和注入依赖项,提高代码的可测试性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值