EventFlow 自定义输出中的依赖注入
Dependency Injection in EventFlow custom output
我正在使用 EventFlow 来跟踪 ETW 事件。为此,我创建了一个充当侦听器的 ASP Net Core 服务。我在我的配置文件中配置了我自己的自定义输出。这些是我的输出和我的 OutputFactory classes:
class CustomOutput : IOutput
{
public Task SendEventsAsync(IReadOnlyCollection<EventData> events, long transmissionSequenceNumber, CancellationToken cancellationToken)
{
foreach(var e in events)
{
//...;
}
return Task.CompletedTask;
}
}
class CustomOutputFactory : IPipelineItemFactory<CustomOutput>
{
public CustomOutput CreateItem(IConfiguration configuration, IHealthReporter healthReporter)
{
return new CustomOutput();
}
}
此 CustomOutput 仅在开始时(创建 EventFlow 管道时)实例化一次,并用于所有事件。主要方法是这样的:
private static void Main()
{
try
{
using (var diagnosticsPipeline = ServiceFabricDiagnosticPipelineFactory.CreatePipeline("MyApplication-MyService-DiagnosticsPipeline"))
{
ServiceRuntime.RegisterServiceAsync("Stateless1Type",
context => new Stateless1(context)).GetAwaiter().GetResult();
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Stateless1).Name);
Thread.Sleep(Timeout.Infinite);
}
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
配置文件中引用了输出和工厂输出类型eventFlowConfig.json:
"extensions": [
{
"category": "outputFactory",
"type": "CustomOutput",
"qualifiedTypeName": "MyProyect.Service.MyApp.SqlOutputFactory, MyProyect.Service.MyApp"
}
]
参考:Event aggregation and collection using EventFlow
因此,实例是在我的程序 class 的主要方法中创建的,也就是说,在调用我的 Startup 配置方法之前。
如果容器在实例化时仍然不存在,我如何从我的输出 class 访问我的依赖容器服务?
目前我已经创建了一个 IServiceCollection 类型的静态 属性 并且我从我的启动配置方法(使用 setter 注入)设置它。我不喜欢这个解决方案,因为我不应该使用对服务的静态访问,但我不知道其他解决方案。这是一种有效的做法吗?
class CustomOutput : IOutput
{
public static IServiceCollection Services { get; set; }
public Task SendEventsAsync(IReadOnlyCollection<EventData> events, long transmissionSequenceNumber, CancellationToken cancellationToken)
{
var sp = Services.BuildServiceProvider();
var loggerFactory = sp.GetService<ILoggerFactory>();
logger = loggerfactory.CreateLogger<CustomOutput>();
var repository = serviceProvider.GetService<IMyRepository>();
foreach (var e in events)
{
logger.LogDebug("event...");
repository.SaveEvent(e);
//...;
}
return Task.CompletedTask;
}
}
public class Startup
{
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//..
CustomOutput.Services = services;
//..
}
}
虽然使用 Explicit Dependency Principle 而不是当前正在实施的服务定位器模式将是更好的选择。目标框架的可扩展性的限制使这变得困难。
只留下静态访问器作为可能解决方案的扩展点。
由于 CustomOutput
只会创建一次,因此遵循单例模式应该适用于此设计
public class CustomOutput : IOutput {
private static Lazy<CustomOutput> instance =
new Lazy<CustomOutput>(() => return new CustomOutput());
private Lazy<ILogger> logger;
private Lazy<IMyRepository> repository;
private CustomOutput() { }
public static CustomOutput Instance {
get {
return instance.Value;
}
}
public void Configure(Lazy<ILogger> logger, Lazy<IMyRepository> repository) {
this.logger = logger;
this.repository = repository
}
public Task SendEventsAsync(IReadOnlyCollection<EventData> events, long transmissionSequenceNumber, CancellationToken cancellationToken) {
//TODO: Add null check and fail if not already configured.
foreach (var e in events) {
logger.Value.LogDebug("event...");
repository.Value.SaveEvent(e);
//...;
}
return Task.CompletedTask;
}
}
public class CustomOutputFactory : IPipelineItemFactory<CustomOutput> {
public CustomOutput CreateItem(IConfiguration configuration, IHealthReporter healthReporter) {
return CustomOutput.Instance;
}
}
在上述方法中,依赖项的创建和注入可以推迟到 Startup
。以下扩展有助于此。
public static class CustomOutputServiceCollectionExtensions {
public IServiceCollection ConfigureCustomOutput(this IServiceCollection services) {
services.AddTransient<IMyRepository, MyRepository>();
var logger = new Lazy<ILogger>(() => {
var sp = services.BuildServiceProvider();
return sp.GetService<ILogger<CustomOutput>>();
});
var repository = new Lazy<IMyRepository>(() => {
var sp = services.BuildServiceProvider();
return sp.GetService<IMyRepository>();
});
CustomOutput.Instance.Configure(logger, repository);
return services;
}
}
然后将从 Startup
;
调用
public class Startup {
//...
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
//...
services.ConfigureCustomOutput();
//...
}
}
我正在使用 EventFlow 来跟踪 ETW 事件。为此,我创建了一个充当侦听器的 ASP Net Core 服务。我在我的配置文件中配置了我自己的自定义输出。这些是我的输出和我的 OutputFactory classes:
class CustomOutput : IOutput
{
public Task SendEventsAsync(IReadOnlyCollection<EventData> events, long transmissionSequenceNumber, CancellationToken cancellationToken)
{
foreach(var e in events)
{
//...;
}
return Task.CompletedTask;
}
}
class CustomOutputFactory : IPipelineItemFactory<CustomOutput>
{
public CustomOutput CreateItem(IConfiguration configuration, IHealthReporter healthReporter)
{
return new CustomOutput();
}
}
此 CustomOutput 仅在开始时(创建 EventFlow 管道时)实例化一次,并用于所有事件。主要方法是这样的:
private static void Main()
{
try
{
using (var diagnosticsPipeline = ServiceFabricDiagnosticPipelineFactory.CreatePipeline("MyApplication-MyService-DiagnosticsPipeline"))
{
ServiceRuntime.RegisterServiceAsync("Stateless1Type",
context => new Stateless1(context)).GetAwaiter().GetResult();
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Stateless1).Name);
Thread.Sleep(Timeout.Infinite);
}
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
配置文件中引用了输出和工厂输出类型eventFlowConfig.json:
"extensions": [
{
"category": "outputFactory",
"type": "CustomOutput",
"qualifiedTypeName": "MyProyect.Service.MyApp.SqlOutputFactory, MyProyect.Service.MyApp"
}
]
参考:Event aggregation and collection using EventFlow
因此,实例是在我的程序 class 的主要方法中创建的,也就是说,在调用我的 Startup 配置方法之前。
如果容器在实例化时仍然不存在,我如何从我的输出 class 访问我的依赖容器服务?
目前我已经创建了一个 IServiceCollection 类型的静态 属性 并且我从我的启动配置方法(使用 setter 注入)设置它。我不喜欢这个解决方案,因为我不应该使用对服务的静态访问,但我不知道其他解决方案。这是一种有效的做法吗?
class CustomOutput : IOutput
{
public static IServiceCollection Services { get; set; }
public Task SendEventsAsync(IReadOnlyCollection<EventData> events, long transmissionSequenceNumber, CancellationToken cancellationToken)
{
var sp = Services.BuildServiceProvider();
var loggerFactory = sp.GetService<ILoggerFactory>();
logger = loggerfactory.CreateLogger<CustomOutput>();
var repository = serviceProvider.GetService<IMyRepository>();
foreach (var e in events)
{
logger.LogDebug("event...");
repository.SaveEvent(e);
//...;
}
return Task.CompletedTask;
}
}
public class Startup
{
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//..
CustomOutput.Services = services;
//..
}
}
虽然使用 Explicit Dependency Principle 而不是当前正在实施的服务定位器模式将是更好的选择。目标框架的可扩展性的限制使这变得困难。
只留下静态访问器作为可能解决方案的扩展点。
由于 CustomOutput
只会创建一次,因此遵循单例模式应该适用于此设计
public class CustomOutput : IOutput {
private static Lazy<CustomOutput> instance =
new Lazy<CustomOutput>(() => return new CustomOutput());
private Lazy<ILogger> logger;
private Lazy<IMyRepository> repository;
private CustomOutput() { }
public static CustomOutput Instance {
get {
return instance.Value;
}
}
public void Configure(Lazy<ILogger> logger, Lazy<IMyRepository> repository) {
this.logger = logger;
this.repository = repository
}
public Task SendEventsAsync(IReadOnlyCollection<EventData> events, long transmissionSequenceNumber, CancellationToken cancellationToken) {
//TODO: Add null check and fail if not already configured.
foreach (var e in events) {
logger.Value.LogDebug("event...");
repository.Value.SaveEvent(e);
//...;
}
return Task.CompletedTask;
}
}
public class CustomOutputFactory : IPipelineItemFactory<CustomOutput> {
public CustomOutput CreateItem(IConfiguration configuration, IHealthReporter healthReporter) {
return CustomOutput.Instance;
}
}
在上述方法中,依赖项的创建和注入可以推迟到 Startup
。以下扩展有助于此。
public static class CustomOutputServiceCollectionExtensions {
public IServiceCollection ConfigureCustomOutput(this IServiceCollection services) {
services.AddTransient<IMyRepository, MyRepository>();
var logger = new Lazy<ILogger>(() => {
var sp = services.BuildServiceProvider();
return sp.GetService<ILogger<CustomOutput>>();
});
var repository = new Lazy<IMyRepository>(() => {
var sp = services.BuildServiceProvider();
return sp.GetService<IMyRepository>();
});
CustomOutput.Instance.Configure(logger, repository);
return services;
}
}
然后将从 Startup
;
public class Startup {
//...
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
//...
services.ConfigureCustomOutput();
//...
}
}