NetCore 依赖注入【一个New就能搞定的事情,为啥非得搞出这么多代码】

十年河东,十年河西,莫欺少年穷

学无止境,精益求精

有如下接口及继承实现类

    interface Idog
    {
        public string name { get; set; }
        void sayhi();
    }

    class dogTom : Idog
    {
        public string name { get; set; }
        public void sayhi()
        {
            Console.WriteLine("say hi from "+this.name);
        }
    }

1、New 的写法 

        static void Main(string[] args)
        {
            Idog idog = new dogTom();
            idog.name = "汤姆";
            idog.sayhi(); 
            Console.Read();
        }

2、依赖注入写法

项目引用:Microsoft.Extensions.DependencyInjection 
并 using Microsoft.Extensions.DependencyInjection;

依赖注入写法:

        static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection(); 
            services.AddScoped<Idog, dogTom>();
            using (ServiceProvider provider = services.BuildServiceProvider())
            {
                var dog = provider.GetService<Idog>();
                dog.name = "tom";
                dog.sayhi(); 
            }
        }

两处代码比对,new更简单,那为啥微软要搞出依赖注入呢?

2.1、依赖注入提供了三种不同生命周期的对象 Singleton、Scoped、Transient

        static void  Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection(); 
            services.AddTransient<Idog, dogTom>();
            using (ServiceProvider provider = services.BuildServiceProvider())
            {
                var dog1 = provider.GetService<Idog>();
                dog1.name = "Tom";
                dog1.sayhi();
                // 
                var dog2 = provider.GetService<Idog>();
                dog2.name = "汤姆";
                dog2.sayhi(); 
                var bol = object.ReferenceEquals(dog1, dog2);
                if (bol)
                {
                    Console.WriteLine("dog 和 do2 是同一个对象");
                }
                else
                {
                    Console.WriteLine("dog 和 do2 不是同一个对象");
                } 
            }
        }
View Code

 

 上述代码中的 AddTransient 改成 AddSingleton,两个对象是同一个对象么?

 services.AddTransient<Idog, dogTom>();
  改成
 services.AddSingleton<Idog, dogTom>();
static void  Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection(); 
            services.AddSingleton<Idog, dogTom>();
            using (ServiceProvider provider = services.BuildServiceProvider())
            {
                var dog1 = provider.GetService<Idog>();
                dog1.name = "Tom";
                dog1.sayhi();
                // 
                var dog2 = provider.GetService<Idog>();
                dog2.name = "汤姆";
                dog2.sayhi(); 
                var bol = object.ReferenceEquals(dog1, dog2);
                if (bol)
                {
                    Console.WriteLine("dog 和 do2 是同一个对象");
                }
                else
                {
                    Console.WriteLine("dog 和 do2 不是同一个对象");
                } 
            }
        }
View Code

 

 由此可见,通过依赖注入实现单利模式相当简单

除了 Singleton、Transient 外,NetCore还提供了 Scoped 【一定作用范围的对象】,Scoped怎么使用呢?

        static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection(); 
            services.AddScoped<Idog, dogTom>();
            using (ServiceProvider provider = services.BuildServiceProvider())
            {
                using (IServiceScope scope = provider.CreateScope())
                {
                    var dog = scope.ServiceProvider.GetRequiredService<Idog>();
                    dog.name = "tom";
                    dog.sayhi();
                    //
                    var dog2 = scope.ServiceProvider.GetService<Idog>();
                    dog2.name = "汤姆";
                    dog2.sayhi();
                    var bol = object.ReferenceEquals(dog, dog2);
                    if (bol)
                    {
                        Console.WriteLine("dog 和 do2 是同一个对象");
                    }
                    else
                    {
                        Console.WriteLine("dog 和 do2 不是同一个对象");
                    }

                }
            }
        }
View Code

 

 依赖注入除了提供了上述三种声明周期的对象外,还实现了链式依赖。

何为链式依赖?

2.2、链式依赖

看下面代码

using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace ConsoleApp2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection(); 
            services.AddScoped<Idog, dogTom>();
            services.AddScoped<Ilog, logService>();
            using (ServiceProvider provider = services.BuildServiceProvider())
            {
                using (IServiceScope scope = provider.CreateScope())
                {
                    var dog = scope.ServiceProvider.GetRequiredService<Idog>();
                    dog.name = "tom";
                    dog.sayhi();
                    //
                    var dog2 = scope.ServiceProvider.GetService<Idog>();
                    dog2.name = "汤姆";
                    dog2.sayhi();
                    var bol = object.ReferenceEquals(dog, dog2);
                    if (bol)
                    {
                        Console.WriteLine("dog 和 do2 是同一个对象");
                    }
                    else
                    {
                        Console.WriteLine("dog 和 do2 不是同一个对象");
                    }

                }
            }
        } 
    }

    interface Idog
    {
        public string name { get; set; }
        void sayhi();
    }

    class dogTom : Idog
    {
        public readonly Ilog ilog;
        public dogTom(Ilog ilog)
        {
            this.ilog = ilog;
        }
        public string name { get; set; }
        public void sayhi()
        {
            ilog.logInfo(this.name + "开始自我介绍");
            Console.WriteLine("say hi from "+this.name);
            ilog.logInfo(this.name + "自我介绍完毕");
        }
    }
    
    interface Ilog
    {
        void logInfo(string msg);
    }

    class logService : Ilog
    {
       public void logInfo(string msg)
        {
            Console.WriteLine(msg);
        }
         
    }
}
View Code

 

 Program 类只创建了Idog对象,并没有创建Ilog对象,但是由于Idog对象依赖Ilog对象,因此, Program 类可以完整使用Iilog对象打印日志,这些都是框架完成的

所谓链式依赖可以理解为A依赖B,B依赖B1/B2/B3等,那么A注入B后就会顺势注入B1/B2/B3

2.3、扩展方法

using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace ConsoleApp2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection();
            services.AddDogService();
            services.AddLogService();
            using (ServiceProvider provider = services.BuildServiceProvider())
            {
                using (IServiceScope scope = provider.CreateScope())
                {
                    var dog = scope.ServiceProvider.GetRequiredService<Idog>();
                    dog.name = "tom";
                    dog.sayhi();
                    //
                    var dog2 = scope.ServiceProvider.GetService<Idog>();
                    dog2.name = "汤姆";
                    dog2.sayhi();
                    var bol = object.ReferenceEquals(dog, dog2);
                    if (bol)
                    {
                        Console.WriteLine("dog 和 do2 是同一个对象");
                    }
                    else
                    {
                        Console.WriteLine("dog 和 do2 不是同一个对象");
                    }

                }
            }
        } 
    }

    static class ServiceExtensions
    {
        public static void AddDogService(this IServiceCollection services)
        { 
            services.AddScoped<Idog, dogTom>();
        }

        public static void AddLogService(this IServiceCollection services)
        {
            services.AddScoped<Ilog, logService>();
        }
    }

    interface Idog
    {
        public string name { get; set; }
        void sayhi();
    }

    class dogTom : Idog
    {
        public readonly Ilog ilog;
        public dogTom(Ilog ilog)
        {
            this.ilog = ilog;
        }
        public string name { get; set; }
        public void sayhi()
        {
            ilog.logInfo(this.name + "开始自我介绍");
            Console.WriteLine("say hi from "+this.name);
            ilog.logInfo(this.name + "自我介绍完毕");
        }
    }
    
    interface Ilog
    {
        void logInfo(string msg);
    }

    class logService : Ilog
    {
       public void logInfo(string msg)
        {
            Console.WriteLine(msg);
        }
         
    }
}
View Code

上述代码中扩展方法为:

    static class ServiceExtensions
    {
        public static void AddDogService(this IServiceCollection services)
        { 
            services.AddScoped<Idog, dogTom>();
        }

        public static void AddLogService(this IServiceCollection services)
        {
            services.AddScoped<Ilog, logService>();
        }
    }

在使用时,只需调用扩展方法即可完成依赖注入

            ServiceCollection services = new ServiceCollection();
            services.AddDogService();
            services.AddLogService();

扩展方法的好处是使代码更简洁、架构师只需写好扩展方法,程序员调用即可。

@天才卧龙的博客

posted @ 2022-09-19 16:18  天才卧龙  阅读(215)  评论(0编辑  收藏  举报