New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simplify ServiceCollection integration for (non-ASP) .NET Core projects #639
Comments
Branch feature-639 created. |
I added a new SimpleInjector.Integration.ServiceCollection NuGet package that contains Here's an example of the use, while configuring a pooled Entity Framework DbContext in the public class MainService
{
public MainService(AdventureWorksContext context) { }
}
public void Main()
{
var container = new SimpleInjector.Container();
var provider = new ServiceCollection()
.AddDbContextPool<AdventureWorksContext>(options => { /*options */ })
// New extension method. Sets up the basic configuration that allows Simple Injector to be
// used in frameworks that require the use of IServiceCollection for registration of
// framework components.
.AddSimpleInjector(container)
.BuildServiceProvider(true);
// New extension method. Finalizes the configuration of Simple Injector on top of
// IServiceCollection. Ensures framework components can be injected into
// Simple Injector-resolved components.
provider.UseSimpleInjector(container);
container.Register<MainService>();
container.Verify();
using (AsyncScopedLifestyle.BeginScope(container))
{
var service = container.GetInstance<MainService>();
service.DoSomething();
}
} This new NuGet package is now in beta and I would like to urge anyone interested to give it a test run and send feedback about its working and its API. I am very interested to see different use cases and integration scenarios than the one given above. |
It seems to be working fine, i just have one question. In old code, using |
Ok, i got it working by injecting |
I found a little issue. When I try to use HostedService the dependencies doesn't get injected. I register HostedService like this: .ConfigureServices((hostContext, services) =>
{
services.AddHostedService<HostedService>();
services.AddSimpleInjector(container);
}) And hosted service constructor looks like this: internal class HostedService : IHostedService
{
public HostedService(
ILogger<HostedService> logger, ApplicationSettings settings,
IFilesInfrastructure filesInfrastructure, WcfService wcfService,
ThreadsManager threadsManager)
{
this.logger = logger;
this.settings = settings;
this.filesInfrastructure = filesInfrastructure;
this.wcfService = wcfService;
this.threadsManager = threadsManager;
}
} Stack Trace:
|
Injecting |
See this: container.RegisterSingleton<MyHostedService>();
services.AddSingleton<Microsoft.Extensions.Hosting.IHostedService>(
_ => container.GetInstance<MyHostedService>()); |
I've been looking at this for a couple of days was making it way too hard for myself trying to follow everything that was said in all of the related issues etc. Got things mainly working except I'm using the .ConfigureServices((hostContext, services) =>
{
var container = BuildContainer();
services.AddSimpleInjector(container);
services.AddSingleton<IHostedService>(container.GetInstance<QueueListenerService>());
services.AddLogging(c => { c.AddSerilog(); });
}) the Any help on this would be great, it's been a while since I've been this stuck / confused!! |
@matt-lethargic Use this instead https://github.com/serilog/serilog-extensions-hosting . Works for me just fine |
Thanks for your feedback. It's very important for me to know when you're confused. This helps me decide which information to add to the documentation. Logging should definately be one.
There is Stack Overflow answer that describes how to integrate Microsoft.Extensions.Logging into Simple Injector, see here. This will probably work just as well with Serilog, but also take a look at this question, which is specifically about Serilog. |
@NekroMancer Funnily enough I was using that extension as well I had Here's most of my Program.cs static async Task<int> Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.Enrich.FromLogContext()
.CreateLogger();
try
{
Log.Information("Starting");
await CreateHostBuilder(args).Build().RunAsync();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return new HostBuilder()
.ConfigureHostConfiguration(configHost =>
{
configHost.SetBasePath(Directory.GetCurrentDirectory());
configHost.AddJsonFile("hostsettings.json", optional: true);
configHost.AddEnvironmentVariables();
configHost.AddCommandLine(args);
})
.ConfigureAppConfiguration(config =>
{
config.AddJsonFile("appsettings.json", optional: false);
config.AddEnvironmentVariables();
config.AddCommandLine(args);
})
.ConfigureServices((hostContext, services) =>
{
var container = BuildContainer();
//services.Configure<AppConfig>(hostContext.Configuration.GetSection("AppConfig"));
services.AddSimpleInjector(container);
services.AddSingleton<IHostedService>(container.GetInstance<QueueListenerService>());
})
.ConfigureLogging(config =>
{
})
.UseSerilog();
}
private static Container BuildContainer()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.RegisterInstance(typeof(IServiceProvider), container);
container.RegisterSingleton<QueueListenerService>();
container.Register<ICommandDispatcher, CommandDispatcher>();
container.Register<IQueueListener, ServiceBusQueueListener>();
container.Register<IMessageProcessor, MessageProcessor>();
return container;
} and for context QueueListenerService.cs ctor using Microsoft.Extensions.Logging;
...
public QueueListenerService(ILogger logger, IQueueListener queueListener)
{
_queueListener = queueListener ?? throw new ArgumentNullException(nameof(queueListener));
_logger = _logger ?? throw new ArgumentNullException(nameof(_logger));
} I get exception @dotnetjunkie I've read those SO post and you seem to point towards writing a ILogger interface that belongs to the app and hides the chosen framework, this is what I was trying to do by using the MS ILogger interface from their I think the two bits I'm struggling with are how cross-wiring works, are things registerd in .net IoC available to SI and the other way around and if |
First of all - you don't do any cross-wiring in your sample code. You need to call host = new HostBuilder()
.UseSerilog()
.ConfigureServices((hostContext, services) =>
{
services.AddSimpleInjector(container);
// register my dependencies
[...]
})
.Build();
host.Services.UseSimpleInjector(container);
container.Verify();
host.Start(); And it works fine. You need to call Second - apparently you can't inject non-generic |
The MS
The problem is that But either way, the post also describes how to override Simple Injector's injection behavior to allow using (the non-generic) MS
I consider the injection of Your source, however, is correct. There is no way to do that with the built-in DI Container. There is, however, a reason you choose to use Simple Injector over the simplistic MS.DI container. That's because Simple Injector does allow you to make your code more maintainable and prevents you from having to fallback to code smells and hacks because of the limitation of the used DI Container. So as my SO answer shows, you can (and arguably should) inject a non-generic |
So I'm just doing what would of been easier in the first place, I've ripped out MS ILogger, written my own logging interface that belongs to the core of the solution and implementing it at the Composition Root and injecting that with SI leaving MS.DI alone as much as possible. |
After taking a close look at both your responses, and taking a good look again at the .NET Generic Host documentation, I came to the conclusion that it might be good if another integration package gets added; one specific for working with Generic Hosts. The new SimpleInjector.Integration.ServiceCollection package simplifies cross wiring on top of third-party libraries that integrate with To give you an idea of the integration model I'm thinking of right now, here is a visual representation of this model: This diagram shows the model that is already in place with the SimpleInjector.Integration.ServiceCollection package added in v4.6.0-beta1. The green dashed parts, however, are things that I think should be added. This means that a new SimpleInjector.Integration.GenericHost package should be added that sits in between Integration.ServiceCollection and Integration.AspNetCore. Here's a code-centric overview of what I think the end result should be: Short-running .NET Core Console with only Simple InjectorExample:public void Main()
{
var container = new Container();
container.Register<MainService>();
container.Verify();
var service = container.GetInstance<MainService>();
service.DoAwesomeStuff();
} Dependencies:
Explanation:For a true console application, integration with Short-running .NET Core Console with only Simple Injector using scopingExample:public void Main()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<MainService>(Lifestyle.Scoped);
container.Verify();
using (AsyncScopedLifestyle.BeginScope(container))
{
var service = container.GetInstance<MainService>();
service.DoAwesomeStuff();
}
} Dependencies:
Explanation:This example shows a short-running console application, that doesn't need to integrate with .NET Core Console with Simple Injector integrated with .NET Core framework componentsExample:public void Main()
{
var container = new Container();
var services = new ServiceCollection()
// Add all framework components you wish to inject into app components, for instance:
.AddDbContextPool<AdventureWorksContext>(options => { /*options */ })
.AddLogging()
.AddOptions()
// Integrate with Simple Injector
.AddSimpleInjector(container);
services
.BuildServiceProvider(true)
.UseSimpleInjector(container, options => // enables auto cross wiring.
{
// Allow application components to depend on Ms.Extensions.Logging.ILogger
options.UseLogging();
});
container.Verify();
using (AsyncScopedLifestyle.BeginScope(container))
{
var service = container.GetInstance<MainService>();
service.DoAwesomeStuff();
}
} Dependencies:
Explanation:In this example, the application requires working with framework components that can't easily be configured without The .NET Core Generic Host with Simple Injector integrationExample:public static async Task Main(string[] args)
{
var container = new Container();
IHost host = new HostBuilder()
.ConfigureHostConfiguration(configHost => { ... })
.ConfigureAppConfiguration((hostContext, configApp) => { ... })
.ConfigureServices((hostContext, services) =>
{
services.AddSimpleInjector(container, options =>
{
// Hooks hosted services into the Generic Host pipeline
// while resolving them through Simple Injector
options.AddHostedService<TimedHostedService>();
});
})
.ConfigureLogging((hostContext, configLogging) => { ... })
.UseConsoleLifetime()
.Build()
.UseSimpleInjector(container, options =>
{
options.UseLogging();
});
container.Verify();
await host.RunAsync();
} Dependencies:
Explanation:The .NET Generic Host model is tightly coupled with the ASP.NET Core (MVC) with Simple Injector integrationpublic void ConfigureServices(IServiceCollection services)
{
services
.AddSimpleInjector(container, options =>
{
options.AddHostedService<TimedHostedService>();
options.AddAspNetCore() // Adds request scoping
.AddControllerActivation()
.AddViewComponentActivation()
.AddPageModelActivation()
.AddTagHelperActivation();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSimpleInjector(container, options =>
{
options.UseLogging();
// Middleware integration
options.UseMiddleware<CustomMiddleware1>(app);
});
} Dependencies:
Explanation:This allows using the integration extensions inside ASP.NET Core's |
…UseLogger() extension method. #639
I pushed Simple Injector v4.6.0-beta2 to NuGet. This beta adds:
This beta release also contains the other v4.6 features that are currently marked as closed. I would urge everyone reading this to check this out and supply me with feedback. |
Just tried this out with a Generic Host and it works perfectly so far, even when using the Microsoft |
Works great for me too. Thanks :) |
Thank you for your feedback. Much appreciated. This gives me confidence about the usefulness and correctness of the new API. In the meantime, I've been working on the documentation for this new API. Preview versions of these new integration guides can be found here:
Your feedback on these pages is very welcome. Thanks in advance |
See: https://stackoverflow.com/questions/52111898/
Currently, cross wiring in Simple Injector is deeply integrated into ASP.NET Core. This means, for instance, that when you have a third-party component (such as Entity Framework Core) that is deeply integrated into
IServiceCollection
, it might be more involved to get this working in a simple .NET Core console application.We should strive to streamline the experience for .NET Core users who don't use ASP.NET Core.
The text was updated successfully, but these errors were encountered: