zoukankan      html  css  js  c++  java
  • .Net Core3.0依赖注入DI

    构建ASP.NET Core应用程序的时候,依赖注入已成为了.NET Core的核心,这篇文章,我们理一理依赖注入的使用方法。

    不使用依赖注入

     首先,我们创建一个ASP.NET Core Mvc项目,定义个表达的爱服务接口,中国小伙类实现这个类如下:

    public interface ISayLoveService
        {
            string SayLove();
        }
    
     public class CNBoyService : ISayLoveService
        {
            public string SayLove()
            {
                return "安红,我喜欢你";
            }
        }

    在LoveController 控制器中调用 ISayLoveService的SayLove方法。

    public class LoveController : Controller
    
        {       
           private  ISayLoveService loveService;     
           public IActionResult Index()
            {
                loveService = new CNBoyService(); //中国小伙对安红的表达
                ViewData["SayLove"] = loveService.SayLove();    
                return View();
            }
        }

    输出如图:

     

    小结:LoveController控制器调用ISayLoveService服务的SayLove方法;我们的做法,直接在控制器去new CNBoyService()实例对象,也就是LoveController依赖ISayLoveService类。

    思考:能不能有种模式,new实例不要在使用的时候进行创建,而是在外部或者有一个容器进行管理;这不就是ioc思想吗?好处,代码的解耦、代码更好的维护等等。

    使用依赖注入

    上面的疑惑,答案是肯定的,有!并且ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式(当然也可以兼容第三方)。我们还使用上面的代码,

    服务注册

     在Startup类ConfigureServices方法中注册服务容器中的依赖关系

      public void ConfigureServices(IServiceCollection services)
    
            {
                services.AddSingleton<ISayLoveService, CNBoyService>();
                services.AddControllersWithViews();
            }

     在LoveControlle控制器中,通过构造函数注入

     private readonly  ISayLoveService loveService;  
    public LoveController(ISayLoveService loveService)
    {

    this.loveService = loveService; }

    public IActionResult Index() {
    ViewData[
    "SayLove"] = loveService.SayLove();

    return View(); }

    LoveController 正在将ISayLoveService作为依赖项注入其构造函数中,然后在Index方法中使用它。

    推荐:

    • 将注入的依赖项分配给只读字段/属性(以防止在方法内部意外为其分配另一个值)。

    • 使用接口或基类抽象化依赖关系实现。

    小结:在控制器中,还有几种使用如:[FromServices] 标签 、 HttpContext.RequestServices.GetService<T>();我们发现可以使用ASP.NET Core 提供了一个内置的服务容器 IServiceProvider。服务只需要在Startup.ConfigureServices 方法中注册,然后在运行时将服务注入 到使用它的类的构造函数中。 框架负责创建依赖关系的实例,并在不再需要时对其进行处理。

    思考:服务注册的时候使用的是 AddSingleton,如services.AddSingleton<ISayLoveService, CNBoyService>();还有其他的吗?

    服务生命周期

    服务注册的时候,ASP.NET Core支持指定三种生命周期如:

    1. Singleton 单例

    2. Scoped 范围

    3. Transient 短暂的

    Singleton 仅创建一个实例。该实例在需要它的所有组件之间共享。因此始终使用同一实例。

    Scoped 每个范围创建一个实例。在对应用程序的每个请求上都会创建一个范围,因此每个请求将创建一次注册为Scoped的任何组件。

    Transient 在每次被请求时都会创建,并且永不共享。

    为了能够更好的裂解生命周期的概念,我们把上面代码稍作改动,做一个测试:

    ISayLoveService 新增个属性LoveId,类型为guid,

    public interface ISayLoveService
        {
            Guid LoveId { get; }
            string SayLove();
        }
        public interface ITransientSayLoveService : ISayLoveService
        {
        }
        public interface IScopedSayLoveService : ISayLoveService
        {
        }
        public interface ISingletonSayLoveService : ISayLoveService
        {
        }
        public interface ISingletonInstanceSayLoveService : ISayLoveService
        {
        }

     BoyService也很简单,在构造函数中传入一个Guid,并对它进行赋值。

    public class BoyService : ITransientSayLoveService,
            IScopedSayLoveService,
            ISingletonSayLoveService,
            ISingletonInstanceSayLoveService
        {
            public BoyService():this(Guid.NewGuid()) { }
            public BoyService(Guid id)
            {
                LoveId = id;
            }
            public Guid LoveId { get; private set; }
    
            public string SayLove()
            {
                return LoveId.ToString();
            }
        }

     每个实现类的构造函数中,我们都产生了一个新的guid,通过这个GUID,我们可以判断这个类到底重新执行过构造函数没有.

    服务注册代码如下:

       public void ConfigureServices(IServiceCollection services)
            {
                //生命周期设置为Transient,因此每次都会创建一个新实例。
                services.AddTransient<ITransientSayLoveService, BoyService>();
                services.AddScoped<IScopedSayLoveService, BoyService>();
                services.AddSingleton<ISingletonSayLoveService, BoyService>();
                services.AddSingleton<ISingletonInstanceSayLoveService>(new BoyService(Guid.Empty));
    
                services.AddControllersWithViews();
            }

    在LifeIndex方法中多次调用ServiceProvider的GetService方法,获取到的都是同一个实例。

      public IActionResult LifeIndex()
            {
                ViewData["TransientSayLove1"] = HttpContext.RequestServices.GetService<ITransientSayLoveService>().SayLove();
                ViewData["ScopedSayLove1"] = HttpContext.RequestServices.GetService<IScopedSayLoveService>().SayLove();
                ViewData["SingletonSayLove1"] = HttpContext.RequestServices.GetService<ISingletonSayLoveService>().SayLove();
                ViewData["SingletonInstanceSayLove1"] = HttpContext.RequestServices.GetService<ISingletonInstanceSayLoveService>().SayLove();
                //同一个HTTP请求 ,在从容器中获取一次
                ViewData["TransientSayLove2"] = HttpContext.RequestServices.GetService<ITransientSayLoveService>().SayLove();
                ViewData["ScopedSayLove2"] = HttpContext.RequestServices.GetService<IScopedSayLoveService>().SayLove();
                ViewData["SingletonSayLove2"] = HttpContext.RequestServices.GetService<ISingletonSayLoveService>().SayLove();
                ViewData["SingletonInstanceSayLove2"] = HttpContext.RequestServices.GetService<ISingletonInstanceSayLoveService>().SayLove();
    
                return View();
            }

    我们编写view页面,来展示这些信息如下:

    @{
        ViewData["Title"] = "LifeIndex";
    }
    
        <div class="row">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h2 class="panel-title">Operations</h2>
                </div>
                <div class="panel-body">
                    <h3>获取第一次</h3>
                    <dl>
                        <dt>Transient1</dt>
                        <dd>@ViewData["TransientSayLove1"] </dd>
                        <dt>Scoped1</dt>
                        <dd>@ViewData["ScopedSayLove1"]</dd>
                        <dt>Singleton1</dt>
                        <dd>@ViewData["SingletonSayLove1"] </dd>
                        <dt>Instance1</dt>
                        <dd>@ViewData["SingletonInstanceSayLove1"]</dd>
                    </dl>
                    <h3>获取第二次</h3>
                    <dl>
                        <dt>Transient2</dt>
                        <dd>@ViewData["TransientSayLove2"]</dd>
                        <dt>Scoped2</dt>
                        <dd>@ViewData["ScopedSayLove2"]</dd>
                        <dt>Singleton2</dt>
                        <dd>@ViewData["SingletonSayLove2"]</dd>
                        <dt>Instance2</dt>
                        <dd>@ViewData["SingletonInstanceSayLove2"]</dd>
                    </dl>
                </div>
            </div>
        </div>

    运行代码第一次输出:

    我们发现,在一次请求中,发现单例、范围的生命周期的guid 没有变化,说明分别用的是同一个对象,而瞬态guid不同,说明对象不是一个。

    刷新之后,查看运行效果

    我们发现通过刷新之后,单例模式的guid还是跟首次看到的一样,其他的都不同;

    总结:如果您将组件A注册为单例,则它不能依赖已注册“作用域”或“瞬态”生存期的组件。一般而言:组件不能依赖寿命短于其寿命的组件。如果默认的DI容器不能满足项目需求,可以替换成第三方的如功能强大的Autofac。

  • 相关阅读:
    C# WPF之Material Design自定义颜色
    C# WPF从RIOT API获取数据(RIOT代表作品《英雄联盟》)
    C# WPF聊天界面(3/3)
    C# WPF简况(2/3)
    C# WPF联系人列表(1/3)
    使用SignalR从服务端主动推送警报日志到各种终端(桌面、移动、网页)
    为什么Node.JS会受到青睐?
    2017级面向对象程序设计——团队作业1
    2017级面向对象程序设计 作业三
    如果抽不出时间写博客怎么办
  • 原文地址:https://www.cnblogs.com/chengtian/p/11769338.html
Copyright © 2011-2022 走看看