由於我們未來系統將使用netCore開發,整理一下今天閱讀netcore DI framework心得。
透過DI技術,我們可以達到物件和client之間的鬆散耦合(降低相依性),
client並不需要知道參考那一個特定實作(new instance),
而是擷取相關實作的Interface(依賴抽象而不是一個Instance,即遵循依賴反轉原則)。
netCore的DI framework由Micorosoft.Extensions.DependencyInjection這nuget package來負責,
主要兩個核心功能就是註冊service(IServiceCollection Interface)和提供service(IServiceProvider Interface),
對於有用過其他DI Framework來說應該很好上手(我之前使用Unity DI),
當我了解IserviceCollection就是container,開發上就可雖心所欲的到處注入,
一般我都會在物件constructor注入相關interface,如下面code
public static class ServiceCollectionExtension
{
public static ServiceCollection SetupKafka(this IServiceCollection services, string configSectionName = "Kafka")
{
services.AddSingleton<Kafka.Public.ILogger, KafkaLoggerWrapper>();
services.AddSingleton<IConfigurationRoot, ConfigurationRoot>();
services.AddSingleton<IJsonConfigSection, JsonConfigSection>();
services.AddSingleton<IKafkaConfig>(new KafkaConfig(new JsonConfigSection(), configSectionName));
services.AddSingleton<IKafkaManager, KafkaManager>();
services.AddSingleton<IJsonSerializationProvider, JsonSerializationProvider>();
return services;
}
}
撰寫ServiceCollection的Extension,註冊自行開發所需的Servervices。
internal sealed class KafkaLoggerWrapper : Kafka.Public.ILogger
{
private readonly NLog.ILogger _logger;
public KafkaLoggerWrapper()
{
LogManager.LoadConfiguration(Path.Combine(Directory.GetCurrentDirectory(), "nlog.config"));
_logger= LogManager.GetLogger("kakfa");
}
void Public.ILogger.LogDebug(string message)
=> _logger.Debug(message);
void Public.ILogger.LogError(string message)
=> _logger.Error(message);
void Public.ILogger.LogInformation(string message)
=> _logger.Info(message);
void Public.ILogger.LogWarning(string message)
=> _logger.Warn(message);
}
這是一個很簡單KafkaLoggerService。
@生命週期
了解Service生命週期相當重要,因為要避免非預期回收Service、初始化成本或client透過Service進行處理相關商業邏輯,
產生非預期結果和錯誤。
Microsoft.Extensions.DependencyInjection.ServiceLifetime提供了三種類型
from MicroSoft
Scoped:同一個scope內,每一個client的request只建立一個(一次)執行個體,
例如DBContext在同一個服務,而這情況下行為,也和Singletion相同。
Singletion:整個應用程式只建立一個執行個體,即每一個client的request都使用相同執行個體。
Transient: client每一個request都會建立執行個體,即GetService都會建立。
Client如何呼叫Service
// setup DI
var serviceProvider = new ServiceCollection()
.SetupKafka()
.BuildServiceProvider();
//using Interface always
var kafkamanager = serviceProvider.GetService<IKafkaManager>();
var kafkaconfig = kafkamanager.GetKafkaClusterConfig("test");
var logger = serviceProvider.GetService<Kafka.Public.ILogger>();
logger.LogDebug("rico");
Console.ReadLine();
@真實世界經歷
抽換Middleware在真實世界很容易遇到(遠大於抽換資料庫),
例如分散式系統將從ServiceBus改成Kafka…等,
我還記得當時要把公司系統上的ServiceBus抽換成Kafka改code經歷,
我很幸運遇到強者同事當時專案規劃,
一律都透過IUnityContainer注入(而非常見執行個體散落在專案中每個地方),
所以EntryPoint範圍整個縮小,當你專案(我們超過95個 projects)大到某一個程度時,
你就會了解,這對剛接觸專案不到兩個月時間的我來說是多麼幸福的事情,
我很開心這一年時間和這位強者同事學到不少.net技術和知識。
參考