I'm using EventFlow to trace ETW events. For this I've created an ASP Net Core service that acts as a listener. I've configured my own custom output in my configuration file. And these are my Output and my 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();
}
}
This CustomOutput is instantiated only one time at start (when EventFlow pipeline is created) and it's used for all the events. The main method is this:
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;
}
}
The output and factory output types are referenced in the configuration file eventFlowConfig.json:
"extensions": [
{
"category": "outputFactory",
"type": "CustomOutput",
"qualifiedTypeName": "MyProyect.Service.MyApp.SqlOutputFactory, MyProyect.Service.MyApp"
}
]
Reference: Event aggregation and collection using EventFlow
Thus, the instance is created in the main method of my Program class, that is, before my Startup configuration methods are invoked.
How could I access from my Output class to my dependency container services if the container still does not exist when it's instantiated?
At the moment I've created a static property of type IServiceCollection and I set it from my Startup config method (using setter injection). I don't like this solution because I shouldn't use static access to services but I don't know another solution. It's this a valid practice?
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;
//..
}
}
While using the Explicit Dependency Principle as opposed to the Service Locator Pattern currently being implemented would be the better choice. Limitations of the target framework's extensibility make this difficult.
The leaves only static accessors as the extensibility point for a possible solution.
Since the
CustomOutput
is only going to be created once, then following a singleton patter should work in this designIn the above approach, the creation and injection of the dependencies can be deferred to the
Startup
. The following extension facilitates this.Which would then be called from
Startup
;