#author("2023-11-20T18:52:22+08:00","default:Admin","Admin")
#author("2023-11-21T19:45:22+08:00","default:Admin","Admin")
[[ASP.NET Core Web]]

&color(red){※This article is based on .NET 7};

#contents

控制反转是一种思想,依赖注入是一种设计模式,控制反转的思想可以利用依赖注入的设计模式实现,反射是依赖注入实现过程的核心技术。

* 什么是DI [#w1d47e17]
Dependency Injection(DI)依赖关系注入是IoC的一种具体实现方式。在DI中,服务提供方和服务使用方之间的耦合关系由第三方组件(DI容器)来处理。容器负责实例化服务,并将其注入到需要该服务的类中。

ASP.NET Core中的DI容器可以帮助我们解决应用程序中的对象管理问题,例如创建对象、管理对象的生命周期等。

DI要做的两个功能是:
- 注册服务
- 注入服务

若要利用 .NET Core DI 框架,你只需引用 Microsoft.Extnesions.DependencyInjection.Abstractions NuGet 包。

如下应用程序代码获得了一个实例:
 ILoggingFactory loggingFactor = serviceProvider.GetService<ILoggingFactory>();
* DI容器的注册 [#g75a14d1]

注册后可以在整个Solution调用
** 注册的概要 [#of8aa89f]

Asp.Net Core 提供了默认的依赖注入(DI:Dependency Injection)&color(red){容器 IServiceCollection,它是一个轻量级的依赖注入容器};

在ASP.NET Core中,我们使用ServiceCollection容器来注册服务。ServiceCollection类继承于IServiceCollection接口,用于注册服务描述符并最终构建出ServiceProvider容器。ServiceCollection提供了一系列方便的方法来简化服务注册的过程。例如,通过调用AddTransient、AddScoped、AddSingleton等方法,我们可以轻松地注册服务。

ServiceCollection会在构建出ServiceProvider容器之前验证注册的服务描述符,以确保其合法性。例如,当注册多个同一服务类型的描述符时,ServiceCollection会抛出异常。另外,在
ServiceCollection.BuildServiceProvider() 方法中,容器将递归检查和解析所有服务描述符,构造出DI框架的核心实例(ServiceProvider)。


IServiceCollection依赖注入生命周期和其他大多数依赖注入容器一样,分为 
- Transient瞬时生命周期,Transient服务在每次被请求时都会被创建一个新的对象。这种生命周期比较适用于轻量级的无状态服务。
- Singleton单例:生命能够周期服务在第一被请求时创建,在后续的每个请求都会使用同一个实例。如果你的应用需要单例服务,推荐的做法是交给服务容器来负责单例的创建和生命周期管理,而不是自己来走这些事情。
- Scoped请求单例:Scoped生命周期的服务是每次web请求被创建,局部单例对象, 在某个局部内是同一个对象(作用域单例,本质是容器单例);一次请求内是一个单例对象,多次请求则多个不同的单例对象.

我们可以在Startup.cs文件中的ConfigureServices方法中直接使用它。

这里我们单独把它拿出来看一下具体怎么使用,我们定义ITestService1,ITestService2,ITestService3,ITestService4以及他们的4个实现类。

#codeprettify{{
IServiceCollection container = new ServiceCollection();
container.AddTransient<ITestService1, TestService1>();//瞬时生命周期 
container.AddSingleton<ITestService2, TestService2>();//单例:全容器都是一个
container.AddScoped<ITestService3, TestService3>();//请求单例:一个请求作用域是一个实例
container.AddSingleton<ITestService4>(new TestService4());//单例生命周期,在整个进程中获取的是相同实例

var provider = container.BuildServiceProvider();
ITestService1 testService1 = provider.GetService<ITestService1>();
ITestService1 testService2 = provider.GetService<ITestService2>();
Console.WriteLine(object.ReferenceEquals(testService1, testService2));//输出 false

ITestService2 testService2_1 = provider.GetService<ITestService2>();
ITestService2 testService2_2 = provider.GetService<ITestService2>();
Console.WriteLine(object.ReferenceEquals(testService2_1, testService2_2));//输出 true

ITestService3 testService3_1 = provider.GetService<ITestService3>();
ITestService3 testService3_2 = provider.GetService<ITestService3>();
Console.WriteLine(object.ReferenceEquals(testService3_1, testService3_2));//输出 true

var scope1 = provider.CreateScope();
var scope2 = provider.CreateScope();
ITestService3 testService3_3 = scope1.ServiceProvider.GetService<ITestService3>();
ITestService3 testService3_4 = scope2.ServiceProvider.GetService<ITestService3>();
Console.WriteLine(object.ReferenceEquals(testService3_3, testService3_4)); //输出 false

ITestService4 testService4_1 = provider.GetService<ITestService4>();
ITestService4 testService4_2 = provider.GetService<ITestService4>();
Console.WriteLine(object.ReferenceEquals(testService4_1, testService4_2)); //输出 true
}}
定义一个Service
#codeprettify{{
public class OrderService : IOrderService
{
    private string guid;

    public OrderService()
    {
        guid = $"时间:{DateTime.Now}, guid={ Guid.NewGuid()}";
    }

    public override string ToString()
    {
        return guid;
    }
}

public interface IOrderService
{
}
}}

注入

#codeprettify{{
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();

        //三种方式注入
        services.AddSingleton<IOrderService, OrderService>();
        services.AddScoped<IOrderService, OrderService>();
        services.AddTransient<IOrderService, OrderService>();
    }
}

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    IOrderService orderService1;
    IOrderService orderService2;

    public WeatherForecastController(IOrderService orderService1, IOrderService orderService2)
    {
        this.orderService1 = orderService1;
        this.orderService2 = orderService2;
    }

    [HttpGet]
    public string Get()
    {
        Debug.WriteLine($"{this.orderService1}\r\n{this.orderService2} \r\n ------");
        return "helloworld";
    }
}
}}

然后画面刷新三次,得到下面的结果

&ref(netcore_DI1.jpg);


** 注册服务的标准实现 [#z26c208b]

就从startup.cs中的ConfigureServices方法说起,先来看下定义: 
#codeprettify{{
public virtual void ConfigureServices (Microsoft.Extensions.DependencyInjection.IServiceCollection services);
}}

* 注入服务 [#q41fb691]

在 ASP.NET Core 中将依赖项注入到控制器
#codeprettify{{
internal class ModuleInit : IModuleInitializer
{
    public void Initialize(IServiceCollection services)
    {
        services.AddScoped<ClassOne>();
    }
}
}}

** 构造函数注入 [#p06a0e26]
在 ASP.NET Core 中将依赖项注入到控制器

服务作为构造函数参数添加,并且运行时从服务容器中解析服务。 通常使用接口来定义服务。 例如,考虑需要当前时间的应用。 

以下接口公开 IDateTime 服务:
#codeprettify{{
public interface IDateTime
{
    DateTime Now { get; }
}
}}

以下代码实现 IDateTime 接口:
#codeprettify{{
public class SystemDateTime : IDateTime
{
    public DateTime Now
    {
        get { return DateTime.Now; }
    }
}
}}

将服务添加到服务容器中:
#codeprettify{{
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDateTime, SystemDateTime>();
    services.AddControllersWithViews();
}
}}

以下代码根据一天中的时间向用户显示问候语:
#codeprettify{{
public class HomeController : Controller
{
    private readonly IDateTime _dateTime;

    public HomeController(IDateTime dateTime)
    {
        _dateTime = dateTime;
    }

    public IActionResult Index()
    {
        var serverTime = _dateTime.Now;
        if (serverTime.Hour < 12)
        {
            ViewData["Message"] = "It's morning here - Good Morning!";
        }
        else if (serverTime.Hour < 17)
        {
            ViewData["Message"] = "It's afternoon here - Good Afternoon!";
        }
        else
        {
            ViewData["Message"] = "It's evening here - Good Evening!";
        }
        return View();
    }
}}

** 批量注入 [#eed0f44f]

#codeprettify{{
private void RegisterService(IServiceCollection services)
{
    var assembly = Assembly.Load("SmartPro.Admin.Logic");
    var allTypes = assembly.GetTypes();
    foreach (var type in allTypes)
    {
        services.AddSingleton(type);
    }
}
}}

* 第三方 [#a2199a8e]

AutoFac也是个容器,下面在Core中把AutoFac整合进来。

在Nuget中添加AutoFac

AutoFac支持AOP

#hr();
コメント:
#comment_kcaptcha

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS