MEF 插件式开发之 DotNetCore 中强大的 DI

C#

浏览数:126

2019-5-17

AD:资源代下载服务

背景叙述

在前面几篇 MEF 插件式开发 系列博客中,我分别在 DotNet FrameworkDotNet Core 两种框架下实验了 MEF 的简单实验,由于 DotNet Framework 由来已久,因此基于该框架下衍生出的很多优秀的 MEF 框架较多。但是对于 DotNet Core 来说,情况有所不同,由于它本身对 DI 内置并提供支持,因此我尝试使用它的全新 依赖注入(DI) 来做一些实验。

动手实验

要想让程序支持 DI,就需要为项目安装 Package:

Install-Package Microsoft.Extensions.DependencyInjection -Version 2.1.1 

然后,我们就可以使用强大的 DI 了。

DotNet Core,所有服务的注册都是统一放到一起的,而这个就是由 ServiceCollection 来接收的;其次,当服务注册完毕后,还需要对服务进行初始化构建,构建后的结果作为一个提供服务者返回,其对应的类型为 ServiceProvider;最后,如果获取某个已经注册的服务的话,可以通过 serviceProvider.GetService () 来获取。

下面,我分别从下面 4 个方面来体验一下 DotNet Core 中强大的 DI

注入并设置服务的生命周期

注册服务需要涉及到服务的生命周期,因此,IServiceCollection 有 3 个不同的扩展方法:

  • AddTransient:每次获取的服务都是新创建的;
  • AddScoped:在一定范围内获取的服务是同一个;
  • AddSingleton:每次获取的服务都是同一个,单例模式的服务;

示例代码如下所示:

public interface IBaseSender
{
    void Send(string message);

}

public interface ITransientSender : IBaseSender { }
public class TransientSender : ITransientSender
{
    public void Send(string message) => Console.WriteLine($"{GetHashCode()} {message}");
}

public interface IScopedSender : IBaseSender { }
public class ScopedSender : IScopedSender
{
    public void Send(string message) => Console.WriteLine($"{GetHashCode()} {message}");
}

public interface ISingletonSender : IBaseSender { }
public class SingletonSender : ISingletonSender
{
    public void Send(string message) => Console.WriteLine($"{GetHashCode()} {message}");
}

class Program
{
    private static readonly object locker = new object();
    static void Main(string[] args)
    {
        var serviceProvider = new ServiceCollection()
            .AddTransient<ITransientSender, TransientSender>()
            .AddScoped<IScopedSender,ScopedSender>()
            .AddSingleton<ISingletonSender, SingletonSender>()
            .BuildServiceProvider();

        using (var scope = serviceProvider.CreateScope())
        {
            for (int i = 0; i < 2; i++)
            {
                serviceProvider.GetService<ITransientSender>().Send("ITransientSender");
                scope.ServiceProvider.GetService<IScopedSender>().Send("IScopedSender");
                serviceProvider.GetService<ISingletonSender>().Send("ISingletonSender");
            }
        }
        Console.WriteLine("***********************************");
        using (var scope = serviceProvider.CreateScope())
        {
            for (int i = 0; i < 2; i++)
            {
                serviceProvider.GetService<ITransientSender>().Send("ITransientSender");
                scope.ServiceProvider.GetService<IScopedSender>().Send("IScopedSender");
                serviceProvider.GetService<ISingletonSender>().Send("ISingletonSender");
            }
        }

        Console.ReadKey();
    }
}

程序输出如下图所示:

通过上图我们可以了解到,

  • 在相同或不同的作用域内,通过 AddTransient 注册的服务每次都是新创建的;
  • 在相同作用域内,通过 AddScoped 注册的服务每次同一个;在不同请求作用域中,通过 AddScoped 注册的服务每次都是新创建的;
  • 通过 AddSingleton 注册的服务在整个程序生命周期内是同一个;

需要注意的是,在 ASP.NET Core 中,所有与 EF 相关的服务都应该通过 AddScoped<TInterface,T> 的方式注入。此外,如果想注入泛型的话,可借助 typeof方式来注入。

构造函数注入

参数注入

public interface IBaseSender
{
    void Send();
}

public class EmialSender : IBaseSender
{
    private readonly string _msg;
    public EmialSender(string msg) => _msg = msg;

    public void Send() => Console.WriteLine($"{_msg}");
}

class Program
{
    static void Main(string[] args)
    {
        var serviceProvider = new ServiceCollection()
            .AddSingleton<IBaseSender, EmialSender>(factory => { return new EmialSender("Hello World"); })
            .BuildServiceProvider();

        serviceProvider.GetService<IBaseSender>().Send();

        Console.ReadKey();
    }
}

服务注入

public interface IBaseSender
{
    void Send();
}

public class EmialSender : IBaseSender
{
    private readonly IWorker _worker;
    public EmialSender(IWorker worker) => _worker = worker;

    public void Send() =>_worker.Run("Hello World");
}

public interface IWorker
{
    void Run(string message);
}

public class Worker : IWorker
{
    public void Run(string message)
    {
        Console.WriteLine(message);
    }
}

class Program
{
    private static readonly object locker = new object();
    static void Main(string[] args)
    {
        var serviceProvider = new ServiceCollection()
            .AddSingleton<IBaseSender, EmialSender>()
            .AddSingleton<IWorker, Worker>()
            .BuildServiceProvider();

        serviceProvider.GetService<IBaseSender>().Send();

        Console.ReadKey();
    }
}

在传统的DotNet 框架下开发,注入是支持 参数、服务和属性的,但是在 DotNet Core 平台下目前只支持前两种注入方式。

添加日志记录

DotNet Core 中已经将 Logger 功能集成进来,只需要安装相应的 Package 即可食用。

Microsoft.Extensions.Logging
Microsoft.Extensions.Logging.Console
Microsoft.Extensions.Logging.Debug

示例程序如下所示:

public interface IBaseSender
{
    void Send();
}

public class EmialSender : IBaseSender
{
    private readonly IWorker _worker;
    private readonly ILogger<EmialSender> _logger;

    public EmialSender(IWorker worker, ILogger<EmialSender> logger)
    {
        _worker = worker;
        _logger = logger;
    }

    public void Send()
    {
        _worker.Run("Hello World");
        _logger.LogInformation(MethodBase.GetCurrentMethod().Name);
    }
}

public interface IWorker
{
    void Run(string message);
}

public class Worker : IWorker
{
    public void Run(string message)
    {
        Console.WriteLine(message);
    }
}

class Program
{
    private static readonly object locker = new object();
    static void Main(string[] args)
    {
        var serviceProvider = new ServiceCollection()
            .AddSingleton<IBaseSender, EmialSender>()
            .AddSingleton<IWorker, Worker>()
            .AddSingleton(new LoggerFactory().AddConsole().AddDebug())
            .AddLogging()
            .BuildServiceProvider();

        serviceProvider.GetService<IBaseSender>().Send();

        Console.ReadKey();
    }
}

总结

这次做的几个小实验还是很有趣的,体验了一下 DotNet Core 中强大的 DI 功能。和传统的 DotNet Framework 相比,有很多改进的地方,这是值得每一个 DotNet 程序员 去尝试的一门新技术。

相关参考

作者:hippieZhou