[TOC] 背景敘述 在前面幾篇 MEF 插件式開發 系列博客中,我分別在 和 兩種框架下實驗了 MEF 的簡單實驗,由於 由來已久,因此基於該框架下衍生出的很多優秀的 MEF 框架較多。但是對於 來說,情況有所不同,由於它本身對 DI 內置並提供支持,因此我嘗試使用它的全新 依賴註入(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): 構造函數的選擇與服務生命周期管理