深入了解.net core的内置IOC容器 core

这篇具有很好参考价值的文章主要介绍了深入了解.net core的内置IOC容器 core。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

ioc     

 嘿嘿! 如何理解IOC呢?我们可以通过一个现实世界的模型来进行解释。比如有一本菜谱这个菜谱就是我们的IServiceCollection,里面记录了菜(Service)描述信息(ServiceDescriptor)菜名(ServiceDescriptor.ServiceType)以及菜具体制作方法(ServiceDescriptor.ImplementationType),通过菜名(ServiceType)告诉厨师(IServiceProvider)制作(实列化、解析)出来我们要吃的菜。这就是IOC技术。

  • 依赖项

    Microsoft.Extensions.DependencyInjection.Abstractions:抽象包,用于扩展容器

    Microsoft.Extensions.DependencyInjection:实现包,实现IOC的基本功能

  • 核心接口 Service:就是我们需要的服务实列(菜)

    ServiceDescriptor:用于描述服务的信息。比如服务名(ServiceType)、实现类(ImplementationType)、生命周期(Lifetime)。(某道菜的制作描述信息)

  • public interface IServiceCollection : ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable, IList<ServiceDescriptor>

    IServiceCollection:是一个IList<ServiceDescriptor>集合,用于保存服务描述信息。(菜谱,记录了很多菜的描述信息)

    IServiceProvider:用于解析服务实列,根容器和子容器实现类不同(厨师)实现类里面有字段用于标记是否是根容器,以及记录所有解析的实列,为将来释放做准备。

    ActivatorUtilities:用于解析一个容器中不存在,但是依赖了容器中的服务的实列。

  • 关键字

    依赖:如果一个类A的构造器中有一个类B的参数,我们说A依赖B

    注入:如果A依赖B,要想实列化A,就必须先实列化B,然后把B载入A的构造器的过程

    依赖注入:IOC容器根据反射得到一个类的依赖关系,自动帮你载入依赖项的过程(注意循环依赖问题

  • 如何避免循环依赖

         循环依赖是指两个或多个模块之间相互依赖,形成了闭环。这种情况下,编译器或运行时环境无法确定模块的加载顺序,会导致编译错误或运行时错误。为了避免循环依赖,可以尝试以下方法:

1. 重新设计架构:重新审视系统的整体设计,尽量避免出现循环依赖的情况。将功能模块划分得更加清晰,降低模块之间的耦合性。

2. 使用依赖注入:通过依赖注入来解决循环依赖问题。将模块的依赖关系交给外部容器管理,并通过构造函数、属性注入等方式将依赖项注入到模块中,而不是在模块内部直接引用依赖的模块。

public class ClassA
{
    public ClassB ClassBInstance { get; set; }

    public void MethodA()
    {
        Console.WriteLine("MethodA in ClassA is called.");
        ClassBInstance.MethodB();
    }
}

public class ClassB
{
    public ClassA ClassAInstance { get; set; }

    public void MethodB()
    {
        Console.WriteLine("MethodB in ClassB is called.");
        ClassAInstance.MethodA();
    }
}
 

3. 抽象接口或基类:通过引入抽象接口或基类来解耦模块之间的直接依赖关系。模块之间通过依赖于接口或基类,而不是直接依赖于具体实现类,降低了循环依赖的可能性。

// Interface IA
public interface IA {
    void setB(B b);
}

// Interface IB
public interface IB {
    void setA(A a);
}

// Class A
public class A implements IA {
    private B b;

    @Override
    public void setB(B b) {
        this.b = b;
    }
}

// Class B
public class B implements IB {
    private A a;

    @Override
    public void setA(A a) {
        this.a = a;
    }
}
 

4. 重新组织模块结构:如果存在循环依赖,可以考虑重新组织模块的结构,将相互依赖的功能提取到独立的模块中,形成单向依赖关系。

5. 使用延迟加载:将模块的加载推迟到真正需要使用时再进行,可以避免在模块加载阶段出现循环依赖的问题。

6. 使用中间件或事件驱动:将模块之间的通信和交互通过中间件或事件驱动的方式进行,避免直接的循环依赖。

7. 使用工厂模式或服务定位器:通过引入工厂模式或服务定位器来解决循环依赖问题。通过工厂或定位器来获取需要的模块实例,而不是直接引用。

以上是一些常见的方法来避免循环依赖问题,在实际项目中可以根据具体情况选择合适的策略。同时,保持良好的设计原则和架构规范,以及使用静态代码分析工具等方法也可以帮助发现并解决潜在的循环依赖问题。

二、服务注册

服务描述

public class ServiceDescriptor
{
    //服务类型,解析时通过服务类型查找
    public Type ServiceType { get; }
    //实现类型必须是具体类,不能是抽象类或者接口(必须实现或者继承ServiceType)
    public Type? ImplementationType { get; }
    //描述生命周期
    public ServiceLifetime Lifetime { get; }
    //用于保存工厂
    public Func<IServiceProvider, object>? ImplementationFactory { get; }
    //用于保存单实例
    public object? ImplementationInstance { get; }
}

1.万能方法

//需要安装:Microsoft.Extensions.DependencyInjection
//创建IServiceCollection实列
IServiceCollection services = new ServiceCollection();

//由于IServiceCollection实现了IList<ServiceDescriptor>接口
//因此下面是一个万能公式,其它的都是扩展方法,本质调用的还是这个万能公式,包括委托的方式(他的实现类型是一个委托)
services.Add(new ServiceDescriptor(typeof(IConnection),typeof(SqlDbConnection),ServiceLifetime.Singleton));

2.泛型接口

//泛型接口需要提前知道类型
services.AddSingleton<IDbConnection, SqlDbConnection>();

3.反射接口

//反射的方式在编写框架时十分有用,无反射无框架
services.AddSingleton(typeof(IDbConnection), typeof(SqlDbConnection));

4.委托方式

//当我们构建的对象需要编写逻辑时,委托方式十分有用
services.AddSingleton<IDbConnection, SqlConnection>();

//低级用法
//假设DbContext依赖IDbConnection,并且需要一个name
//sp是一个IServiceProvider的实列
//委托方式在注册的同时还能进行预解析
//sp到底是根容器还是子容器由解析时的IServiceProvider
services.AddSingleton(sp =>
{    
    var connection = sp.GetRequiredService<IDbConnection>();
    return new DbContext(connection, "c1");
});  

//高级用法
services.AddSingleton(sp =>
{        
    return ActivatorUtilities.CreateInstance<DbContext>(sp,"c1");
}); 

5.泛型注册

//注册泛型时,只能使用反射接口,并且泛型参数不要写入,解析时来确立,如果有多个泛型参数使用逗号隔开
services.AddSingleton(typeof(ILogger<>), typeof(ConsoleLogger<>));    

6.尝试注册

//如果IDbConnection已注册则后续的services.TryAddSingleton(typeof(IDbConnection), typeof(SqlDbConnection));不会注册新的实现
services.TryAddSingleton(typeof(IDbConnection), typeof(SqlDbConnection));  

 7.默认注册

IServiceCollection services = new ServiceCollection();
var sp = services.BuildServiceProvider();
var sp1 = sp.GetRequiredService<IServiceProvider>();

三、构建容器

IServiceProvider container = services.BuildServiceProvider(new ServiceProviderOptions 
{
     ValidateOnBuild = true,//构建时检查是否有依赖没有注册的服务
     ValidateScopes = true,//在解析服务时检查是否通过根容器来解析Scoped类型的实列
});

四、服务解析

//如果同一个服务类型,注册多个实现,那么默认获取最后一个实现。
services.AddSingleton<IDbConnection, SqlConnection>();
services.AddSingleton<IDbConnection, MySqlConnection>();
IServiceProvider container = services.BuildServiceProvider();
//如果服务未注册,返回null
IDbConnection? connection = container.GetService<IDbConnection>();
//服务不存在讲引发异常
IDbConnection connection = container.GetRequiredService<IDbConnection>();
//获取IDbConnection所有实现
IEnumerable<IDbConnection> connections = container.GetRequiredServices<IDbConnection>();
//假设DbContext依赖IDbConnection,并且需要一个name,但是容器没有注册DbContext
var context = ActivatorUtilities.CreateInstance<DbContext>(container, "c1");

五、生命周期

  • 容器除了会帮我们创建对象,还负责对象的销毁,特别对于托管资源。对于实例化过程部深入解析

  • 不要试图通过根容器来解析Scoped或者Transient生命周期的实列

  • 单实例的对象不能依赖一个Scoped或者Transient生命周期的实列

  • 在Debug模式下可以看到容器是否是根容器,以及容器解析的实列,容器会记录由它解析的所有实列,为释放做准备。

六、组件扫描

组件扫描可以自定义规则,比如根据实现了某个接口,或者统一后缀

这里我们演示如何通过注解来扫描,大家也可以根据接口的方式来扫描

[AttributeUsage(AttributeTargets.Class)]
public class InjectionAttribute : Attribute
{
    public Type? ServiceType { get; set; }
    public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Transient;
}
public static class InjectionIServiceCollectionExtensions
{
    public static IServiceCollection AddServicesByInjection<T>(this IServiceCollection services)
    {
        var serviceTypes = typeof(T).Assembly.GetTypes()
            .Where(a => a.IsClass)
            .Where(a => a.GetCustomAttribute<InjectionAttribute>() != null)//扫描注解
            .Where(a => !a.IsAbstract);
        foreach (var item in serviceTypes)
        {
            var injection = item.GetCustomAttribute<InjectionAttribute>();
            if (injection!.ServiceType == null)
            {
                services.Add(new ServiceDescriptor(item, item, injection.Lifetime));
            }
            else
            {
                services.Add(new ServiceDescriptor(injection!.ServiceType, item, injection.Lifetime));
            }
        }
        return services;
    }
}

public interface IDbConnection
{

}文章来源地址https://www.toymoban.com/news/detail-662502.html

[Injection(ServiceType = typeof(IDbConnection), Lifetime = ServiceLifetime.Scoped)]
public class DbConnection : IDbConnection
{

}

到了这里,关于深入了解.net core的内置IOC容器 core的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • .net core IOC 容器实现(四) -- CallSiteRuntimeResolver

    上一节聊了一下 CallSite 是怎样生成的,这一节我们来看一下 CallSite 是如何使用的。 先让我们来回顾一下 CreateServiceAccessor 这个方法。 这段代码跟 CallSite 有关的一共有三个地方,分别是 GetCallSite 和 Resolve(callSite,Root) 以及 _engine.RealizeService 。其中 GetCallSite 是用来生成 CallSite

    2024年02月16日
    浏览(38)
  • 网络协议的深入了解!

    随着互联网的普及和发展,网络协议在计算机网络中扮演着至关重要的角色。网络协议是计算机网络中不同设备之间进行通信和数据交换的规则和标准。本文将对网络协议的概念、分类以及一些常见的网络协议进行详细的介绍和案例分析。 网络协议是一种规范,它定义了计算

    2024年02月04日
    浏览(31)
  • .net core di ioc

    (Dependency Injection,DI)依赖注入,又称依赖关系注入,是一种软件设计模式,也是依赖倒置原则的一种体现。 依赖倒置原则的含义如下 上层模块不依赖下层模块。二者都依赖抽象 抽象不依赖细节 细节依赖抽象 依赖注入原则有别于传统的通过new直接依赖下层模块的形式,

    2024年02月22日
    浏览(33)
  • 深入了解常见的应用层网络协议

    目录 1. HTTP协议 1.1. 工作原理 1.2. 应用场景 1.3. 安全性考虑 2. SMTP协议 2.1. 工作原理 2.2. 应用场景 2.3. 安全性考虑 3. FTP协议 3.1. 工作原理 3.2. 应用场景 3.3. 安全性考虑 4. DNS协议 4.1. 工作原理 4.2. 应用场景 4.3. 安全性考虑 5. 安全性考虑与未来发展趋势 5.1. TLS/SSL的普及 5.2. 新兴

    2024年01月17日
    浏览(46)
  • 深入了解 OkHttp 协议:优雅的网络请求框架

    😄作者简介: 小曾同学.com,一个致力于测试开发的博主⛽️,主要职责:测试开发、CI/CD 如果文章知识点有错误的地方,还请大家指正,让我们一起学习,一起进步。😊 座右铭:不想当开发的测试,不是一个好测试✌️。 如果感觉博主的文章还不错的话,还请点赞、收藏哦

    2024年02月08日
    浏览(54)
  • net core内置日志

    引入 Microsoft.Extensions.Logging 命名空间:用于定义和实现日志记录功能 ILogger接口 a. 内置日志系统的主要接口,用于记录日志信息,可以通过一来注入将ILogger实例注入到记录日志的类中,并使用它来记录不同级别的日志消息 ILoggerFactory接口 a. 用于创建ILogger实例工厂接口,可以

    2024年02月16日
    浏览(35)
  • 【网络编程】深入了解UDP协议:快速数据传输的利器

    (꒪ꇴ꒪ ),Hello我是 祐言QAQ 我的博客主页:C/C++语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍 快上🚘,一起学习,让我们成为一个强大的攻城狮! 送给自己和读者的一句鸡汤🤔: 集中起来的意志可以击穿顽石! 作者水平很有限,如果发现错误,请在评论区指

    2024年02月09日
    浏览(50)
  • .Net Core后端架构实战【3-介入IOC控制反转】

    摘要:基于.NET Core 7.0WebApi后端架构实战【2-介入IOC控制反转】  2023/04/09, ASP.NET Core 7.0, VS2022 Dependency Injection,何为依赖注入?由容器动态的将对象依赖的资源注入到对象之中。假设容器在构造对象A时,对象A的构造依赖对象B、对象C、对象D这些参数,容器会将这些依赖关系自

    2024年02月07日
    浏览(41)
  • .NET6.0实现IOC容器

    IOC 的作用这里省略…只对如何使用进行说明。 这里使用 .NET6.0 WebAPI 应用 下面是在 program 类中的代码 通过在 Controller 的构造函数中注入 IAuthService 启动后,通过 swagger 发起请求,验证接口。 基本 IOC容器 流程已实现。但是这样存在一个弊端,每个接口和实现都要在 program 中手

    2024年02月10日
    浏览(43)
  • 深入分析Spring的IoC容器:从底层源码探索

    前言: 博主在最近的几次面试中,大中小厂都问到了Spring的ioc容器相关问题,这块知识确实是面试中的重点内容,因此结合所看的书籍,在这篇文章中总结下。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读,感谢大佬的关注。 如果文章有什么需要改进的地方欢迎

    2024年02月12日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包