【问题标题】:Dependency Injection in EventFlow custom outputEventFlow 自定义输出中的依赖注入
【发布时间】:2018-09-26 14:07:49
【问题描述】:

我正在使用 EventFlow 来跟踪 ETW 事件。为此,我创建了一个充当侦听器的 ASP Net Core 服务。我已经在我的配置文件中配置了我自己的自定义输出。这些是我的 Output 和 OutputFactory 类:

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

因此,实例是在我的 Program 类的 main 方法中创建的,即在调用我的 Startup 配置方法之前。

如果容器在实例化时仍然不存在,我如何从我的输出类访问我的依赖容器服务?

目前,我创建了一个 IServiceCollection 类型的静态属性,并通过我的 Startup 配置方法(使用 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;
        //..
    }
}

【问题讨论】:

  • IServiceCollection 应该只构建一次。您在 for 循环中重复执行此操作。
  • 显示更多关于需要哪些服务以及如何使用它们的详细信息。这可能是XY problem
  • 你说得对,IServiceCollection 一定是脱离了循环。使用具体服务更新帖子。

标签: c# asp.net-core dependency-injection azure-service-fabric event-flow


【解决方案1】:

虽然使用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();

        //...
    }
}

【讨论】:

  • CustomOutput 在启动方法被调用之前创建。 EventFlow 在 Program 类的 main 方法中实例化 CustomOutput。容器仍然不存在。我无法使用构造函数注入服务。
  • @Alpha75 显示与您如何集成 .net 核心相关的主要方法。一家初创公司。让我们看看我们是否可以找到解决方法
  • 添加了 main 方法。
  • @Alpha75 我仍在审查存储库,但您也应该看看github.com/Azure/diagnostics-eventflow#service-fabric-support
  • 我曾想过解决属性集上的服务。在我看来,像你所做的那样推迟实例更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-04
  • 1970-01-01
  • 2012-11-12
  • 2011-08-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多