MEF 插件式开发之 DotNetCore 中强大的 DI
背景叙述
在前面几篇 MEF 插件式开发 系列博客中,我分别在
DotNet Framework和DotNet 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 程序员 去尝试的一门新技术。
相关参考
- How to register multiple implementations of the same interface in Asp.Net Core?
 - 深入理解net core中的依赖注入、Singleton、Scoped、Transient
 - Multi-tenant Dependency Injection in ASP.NET Core
 - ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
 
MEF 插件式开发之 DotNetCore 中强大的 DI的更多相关文章
- MEF 插件式开发之 DotNetCore 初体验
		
背景叙述 在传统的基于 .Net Framework 框架下进行的 MEF 开发,大多是使用 MEF 1,对应的命名空间是 System.ComponentModel.Composition.在 Do ...
 - MEF 插件式开发之 WPF 初体验
		
MEF 在 WPF 中的简单应用 MEF 的开发模式主要适用于插件化的业务场景中,C/S 和 B/S 中都有相应的使用场景,其中包括但不限于 ASP.NET MVC .ASP WebForms.WPF ...
 - Android安全开发之WebView中的地雷
		
Android安全开发之WebView中的地雷 0X01 About WebView 在Android开发中,经常会使用WebView来实现WEB页面的展示,在Activiry中启动自己的浏览器,或者 ...
 - JavaEE开发之Spring中的多线程编程以及任务定时器详解
		
上篇博客我们详细的聊了Spring中的事件的发送和监听,也就是常说的广播或者通知一类的东西,详情请移步于<JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换&g ...
 - Android插件化开发之OpenAtlas生成插件信息列表
		
上一篇文章.[Android插件化开发之Atlas初体验]( http://blog.csdn.net/sbsujjbcy/article/details/47446733),简单的介绍了使用Atla ...
 - MEF 插件式开发 - 小试牛刀
		
原文:MEF 插件式开发 - 小试牛刀 目录 MEF 简介 实践出真知 面向接口编程 控制反转(IOC) 构建入门级 MEF 相关参考 MEF 简介 Managed Extensibility Fra ...
 - MEF 插件式开发 - WPF 初体验
		
原文:MEF 插件式开发 - WPF 初体验 目录 MEF 在 WPF 中的简单应用 加载插件 获取元数据 依赖注入 总结 MEF 在 WPF 中的简单应用 MEF 的开发模式主要适用于插件化的业务场 ...
 - JavaEE开发之Spring中Bean的作用域、Init和Destroy方法以及Spring-EL表达式
		
上篇博客我们聊了<JavaEE开发之Spring中的依赖注入以及AOP>,本篇博客我们就来聊一下Spring框架中的Bean的作用域以及Bean的Init和Destroy方法,然后在聊一下 ...
 - JavaEE开发之Spring中的条件注解组合注解与元注解
		
上篇博客我们详细的聊了<JavaEE开发之Spring中的多线程编程以及任务定时器详解>,本篇博客我们就来聊聊条件注解@Conditional以及组合条件.条件注解说简单点就是根据特定的条 ...
 
随机推荐
- Java 实现将其他类型数据转换成 JSON 字符串工具类
			
这是网上一个大神实现的,具体出处已找不到,在这做个记录,方便以后使用. package com.wb.test; import java.beans.IntrospectionException; i ...
 - eclipse——JDK安装与环境变量配置步骤
			
第一次接触eclipse的时候,让我自己安装jdk和配置环境变量,我是懵逼的,后来百度到找到了一个比较详细的引导,本人测试没问题,截图按步骤如下: JDK安装 步骤1: 步骤2: 配置环境变量 步 ...
 - 在IIS建立的ftp,可以成功连接登录,但是不显示目录
			
IIS建立FTP站点很简单,不作说明 Windows的防火墙也开通了FTP端口(默认21),Telnet也是通的,在本机可以打开,在局域网其它电脑或外网也可以连接,但就是不显示目录,如果用浏览器打开提 ...
 - 【Spark工作原理】stage划分原理理解
			
Job->Stage->Task开发完一个应用以后,把这个应用提交到Spark集群,这个应用叫Application.这个应用里面开发了很多代码,这些代码里面凡是遇到一个action操作, ...
 - Vue-Cli 搭建项目   小白
			
vue-用Vue-cli从零开始搭建一个Vue项目 Vue是近两年来比较火的一个前端框架(渐进式框架吧). Vue两大核心思想:组件化和数据驱动.组件化就是将一个整体合理拆分为一个一个小块(组件),组 ...
 - 内存管理-slab[原理]
			
前言 主要讲解原理,基于2.6.32版本内核源码.本文整体思路:先由简单内存模型逐渐演进到当下通用服务器面对的内存模型,讨论每一个内存模型下slab设计需要解决的问题. 历史简介 linux内核运行需 ...
 - Python常用模块——json & pickle
			
序列化模块 1.什么是序列化-------将原本的字典,列表等对象转换成一个字符串的过程就叫做序列化 2.序列化的目的 1.以某种存储形式使自定义对象持久化 2.将对象从一个地方传递到另一个地方 3. ...
 - 【LeetCode】13. 罗马数字转整数
			
题目 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为 ...
 - Intellij新安装初始化配置
			
自动编译开关 忽略大小写开关 IDEA默认是匹配大小写,此开关如果未关.你输入字符一定要符合大小写.比如你敲string是不会出现代码提示或智能补充.但是,如果你开了这个开关,你无论输入String或 ...
 - postgresql 表继承
			
可以按如下语句创建表 CREATE TABLE capitals ( name text, population real, altitude int, -- (in ft) state ) ); C ...