依赖注入

被子太轻,压不住想你的心。 --zhu
依赖注入概念
生活中的“控制反转”:
自己发电和用电网的电。
依赖注入(Dependency Injection,DI)是控制反转(Inversion of Control,IOC)思想的实现方式。
依赖注入简化模块的组装过程,降低模块之间的耦合度。

代码控制反转的目的
“怎么创建XX对象”->“我要XX对象”
两种实现方式:
(1)服务定位器(ServiceLocator);

IDbConnection conn=ServiceLocator.GetService<IDbConnection>();

(2)依赖注入(Dependency Injection,DI);

class Demo
{
  public IDbConnection Conn{get;set;}
  public void InsertDB()
  {
    IDbConnection  cmd=Conn.CreateCommand();
  }
}

DI概念
服务(Service):对象;
注册服务;
服务容器:负责管理注册的服务;
查询服务:创建对象及关联对象;
对象生命周期:Transient(瞬态);Scoped(范围);Singleton(单例);

.NET中使用DI
1.Install-Package Microsoft.Extensions.DependencyInjection
2.using Microsoft.Extensions.DependencyInjection
3.ServiceCollection用来构造容器对象IServiceProvider()创建的ServiceProvider,可以用来获取BuildServiceProvider()之前ServiceCollection中的对象。

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection();
            services.AddTransient<TestServiceImpl>();
            //services.AddSingleton<TestServiceImpl>();
            //services.AddScoped<TestServiceImpl>();

            using (ServiceProvider sp = services.BuildServiceProvider())//服务定位器 ServiceProvider
            {
                TestServiceImpl t = sp.GetService<TestServiceImpl>();
                t.Name = "Lily";
                t.SayHi();

                TestServiceImpl t1 = sp.GetService<TestServiceImpl>();
                t1.Name = "Tom";
                t1.SayHi();
                Console.WriteLine(object.ReferenceEquals(t, t1));
                t.SayHi();


                TestServiceImpl tt1;
                using (IServiceScope scope = sp.CreateScope())
                {
                    TestServiceImpl ts= scope.ServiceProvider.GetService<TestServiceImpl>();
                    t.Name = "Lily";
                    t.SayHi();
                    tt1 = ts;
                }

                using (IServiceScope scope = sp.CreateScope())
                {
                    TestServiceImpl ts = scope.ServiceProvider.GetService<TestServiceImpl>();
                    t.Name = "Lily";
                    t.SayHi();
                    Console.WriteLine(object.ReferenceEquals(tt1, ts));
                }
            }

   
                Console.Read();
        }
    }

    internal class TestServiceImpl : ITestService
    {
        public string Name { get ; set; }

        public void SayHi()
        {
            Console.WriteLine($"Hi,I'm {Name}");
        }
    }
    public interface ITestService
    {
        public string Name { get; set; }
        public void SayHi();
    }
}

结果:

Hi,I'm Lily
Hi,I'm Tom
False
Hi,I'm Lily
Hi,I'm Lily
Hi,I'm Lily
False

生命周期
1.给类构造函数中打印,看看不同生命周期的对象创建,使用serviceProvider.CreateScope()创建Scope。
2.如果一个类实现了IDisposable接口,则离开作用域之后,容器会自动调用对象的Dispose方法。
3.不要在长生命周期的对象中引用比它短的生命周期的对象。在.NET Core中,会默认抛异常。
4.生命周期的选择:如果类无状态,建议为Singleton;如果类有状态,且有Scpoe控制,建议为Scoped,因为通常这种Scope控制下的代码都是运行在同一个线程中的,没有并发修改的问题;在使用Transient的时候要严谨。
5..NET注册服务的重载方法很多,多看官方文档。

IServiceProvider的服务定位器方法:
1.T GetService() 如果获取不到对象,则返回null。(多个时,返回最后一个注册的)
object GetService(Type serviceType)
2.T GetRequireService() 如果获取不到对象,则抛异常。
object GetRequireService(Type serviceType)
3.IEnumerable GetServices()适用于可能有很多满足条件的服务。(多个s)
IEnumerable GetServices(Type serviceType)

DI:依赖注入
1.依赖注入是有“传染性”的,如果一个类的对象是通过DI创建的,那么这个类的构造函数中声明的所有服务类型的参数都会被DI赋值;但是如果一个对象是程序员手动创建的,那么这个对象就和DI没有关系,它的构造函数中声明的服务类型参数就不会被自动赋值。
2..NET的DI默认是构造函数注入。
3.举例:编写一个类,连接数据库做插入操作,并且记录日志(模拟输出),把Dao、日志都放入单独的服务类。

 class Program
    {
        static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection();
            services.AddScoped<Controller>();
            services.AddScoped<ILog, LogImpl>();
            services.AddScoped<IStorage, StorageImpl>();
            services.AddScoped<IConfig, ConfigImpl>();


            using (ServiceProvider sp = services.BuildServiceProvider())//服务定位器 ServiceProvider
            {
                var t = sp.GetRequiredService<Controller>();
                t.Test();
            }
            Console.ReadKey();
        }

        class Controller
        {
            private readonly ILog _log;
            private readonly IStorage _storage;
            public Controller(ILog log, IStorage storage)
            {
                _log = log;
                _storage = storage;
            }

            public void Test()
            {
                this._log.Log("开始上传");
                this._storage.Save("12qq", "1.txt");
                this._log.Log("上传完毕");
            }
        }
        interface ILog
        {
            public void Log(string msg);
        }
        class LogImpl : ILog
        {
            public void Log(string msg)
            {
                Console.WriteLine($"日志:{msg}");
            }
        }

        interface IConfig
        {
            public string GetValue(string name);
        }

        class ConfigImpl : IConfig
        {
            public string GetValue(string msg)
            {
                return "hello";
            }
        }


        interface IStorage
        {
            public void Save(string content, string name);
        }
        class StorageImpl : IStorage
        {
            //降低模块之间的耦合
            private readonly IConfig _config;
            public StorageImpl(IConfig config)
            {
                _config = config;
            }

            public void Save(string content, string name)
            {
                string server = _config.GetValue("server");
                Console.WriteLine($"向服务器{server}的文件名为{name}上传{content}");
            }
        }
    }

posted @ 2024-07-07 23:12  小脑虎爱学习  阅读(46)  评论(0)    收藏  举报