1. 程式人生 > >MEF 插件幸運飛艇平臺出租式開發 - DotNetCore 中強大的 DI

MEF 插件幸運飛艇平臺出租式開發 - DotNetCore 中強大的 DI

() string 傳統 debug 還需 inf main 我們 version

背景敘述
在前面幾篇幸運飛艇平臺出租(www.1159880099.com)QQ1159880099 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 程序員 去嘗試的一門新技術。

MEF 插件幸運飛艇平臺出租式開發 - DotNetCore 中強大的 DI