[netCore] DI Framework

由於我們未來系統將使用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技術和知識。

 

 

參考

IServiceCollection Interface

IServiceProvider Interface

Dependency injection in ASP.NET Core