I want to seed some data to my sotre context from the app entry point.
My question is why should i create scope on main method (within program.cs).
Instead of consuming service directly ?
What is the rational behind this best practice ?
Create scope
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var serv = scope.ServiceProvider.GetRequiredService<StoreContext>();
// ..... do stuff
}
host.Run();
}
Consume directly
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
var serv = host.Services.GetRequiredService<StoreContext>();
// do some code here
host.Run();
}
If you've registered your context with a Scoped lifetime (which is the default) then you will get an exception if you try to retrieve it from the root ServiceProvider. You will need to create a IServiceScope first and then request the services from there. This ensures that any dependencies that are also scoped (for example services dependant on the current request in a web app) can be resolved correctly as well.
To be clear: If you request from the root, there is no scope. You need a scope to request a scoped service; without one an exception will be thrown.
From the documentation under the heading "Call services from Main":
Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. This approach is useful to access a scoped service at startup to run initialization tasks
Retrieving "directly" is reserved for singletons. If you register your context with a lifetime of Singleton then retrieving it from the root service provider will work just fine.
// register
services.AddDbContext<StoreContext>(ServiceLifetime.Singleton);
// use
var context = host.Services.GetRequiredService<StoreContext>();
Note that for contexts in a web application this should be avoided. For a console application where there's no concurrent use of the context this approach is fine.
Related
In an ASP.NET Core 6 app, adding IServiceProvider to the service collection in this way allows injection of a scoped provider:
services.AddScoped(sp => sp);
But is it the scoped provider for the current request? In other words, is it guaranteed to provide the same instances of scoped dependencies that may have been injected elsewhere in the request?
XY: Method boundary callbacks are added to the service collection at startup. I'm injecting a service provider rather than the callbacks for two reasons: to break potential circular dependencies; and to avoid instantiating callbacks for all methods when only one method is being called.
First of all you don't need to register service provider explicitly, it should be available without any extra registrations.
Second of all resolving IServiceProvider from the scope, at least ATM (.NET 6) not only returns the same scoped provider, but the same instance:
var services = new ServiceCollection();
services.AddScoped<MyScopedDep>();
var serviceProvider = services.BuildServiceProvider();
using var serviceScope = serviceProvider.CreateScope();
var providerResolvedFromScope = serviceScope.ServiceProvider.GetRequiredService<IServiceProvider>();
Console.WriteLine(object.ReferenceEquals(serviceScope.ServiceProvider, providerResolvedFromScope)); // prints "True"
Console.WriteLine(object.ReferenceEquals(serviceScope.ServiceProvider.GetRequiredService<MyScopedDep>(), providerResolvedFromScope.GetRequiredService<MyScopedDep>())); // prints "True"
And from the docs:
For example, if you resolve services from a scope, and any of those services take an IServiceProvider, it'll be a scoped instance.
I'm using .net core 3.1 with this configuration :
public interface IFoo
{
public void Work();
}
public class Foo : IFoo
{
readonly string MyGuid;
public Foo()
{
MyGuid = Guid.NewGuid().ToString();
}
public void Work() { Console.WriteLine(MyGuid); }
}
And this is the configuration :
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IFoo, Foo>();
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IFoo foo, IServiceProvider myServiceProvider)
{
Console.WriteLine("Via ApplicationServices");
app.ApplicationServices.GetService<IFoo>().Work();
Console.WriteLine("Via IServiceProvider");
myServiceProvider.GetService<IFoo>().Work();
Console.WriteLine("Via IFoo injection");
foo.Work();
}
Result are :
Via ApplicationServices 27e61428-2adf-4ffa-b27a-485b9c45471d <----different
Via IServiceProvider c9e86865-2eeb-44db-b625-312f92533beb
Via IFoo injection c9e86865-2eeb-44db-b625-312f92533beb
More , in the controller action , If I use IserviceProvider :
[HttpGet]
public IActionResult Get([FromServices] IServiceProvider serviceProvider)
{
serviceProvider.GetService<IFoo>().Work();
}
I see another different Guid : 45d95a9d-4169-40a0-9eae-e29e85a3cc19.
Question:
Why does the IServiceProvider and Injection of IFoo in the Configure method yield the same Guid , while the controller action and app.ApplicationServices.GetService yield different ones?
There are 4 different guids in this example
It's a scoped service. It supposed to be the same Guid.
TL;DR;
IFoo foo is resolved using myServiceProvider.GetService<IFoo>() before both get passed into Configure method. So you're resolving same IFoo instance from the same myServiceProvider instance for the 2nd time.
app.ApplicationServices is special root service provider of the application. Parent of myServiceProvider. Thus it resolves different IFoo.
**Default behavior of app.ApplicationServices should be to throw exception when you try to resolve scoped service.
LONGER EXPLANATION
If you have three dummy classes:
class Singleton { }
class Scoped { }
class Transient { }
And you register them as such in IServiceConfiguration:
public static void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Singleton>();
services.AddScoped<Scoped>();
services.AddTransient<Transient>();
}
Now, you create your "root" IServiceProvider from IServiceCollection - in command-line app, it would look like this:
ServiceCollection sc = new ServiceCollection();
ConfigureServices(sc);
ServiceProvider root = sc.BuildServiceProvider();
ROOT SERVICE PROVIDER BEHAVIOR (equivalent to app.ApplicationServices):
If you now test root:
root.GetService<Singleton>(); - every time it's called returns same object instance.
root.GetService<Scoped>(); - every time it's called returns same object instance.
root.GetService<Transient>(); every time it's called returns new object instance.
CHILD-SCOPE SERVICE PROVIDER BEHAVIOR (eg: IServiceProvider in Configure method):
If you now create child scope and use it's own IServiceProvider:
IServiceScope scope1 = root.CreateScope();
IServiceProvider sp1 = scope1.ServiceProvider;
sp1.GetService<Singleton>(); - every time it's called returns same object instance whichroot.GetService<Singleton>(); returns. Singleton is the same instance no matter from which scope you call it. It is resolved climbing the hierarchy of scopes back to the root service provider (scopeless one).
sp1.GetService<Scoped>(); - every time it's called returns same object instance, but not the same instance that root returns. Object instance is cached on the current scope. Every scope creates/caches it's own scoped instance.
sp1.GetService<Transient>(); every time it's called returns new object instance, same behavior like for the root.
root scope is "special" only because it has no parent scope, so resolving scoped or singleton service from the root technically does the same thing - object instance returned is cached in the root itself.
This also explains why you cannot resolve service from IServiceCollection directly. IServiceCollection does not have hierarchy of scopes and caching infrastructure which IServiceProvider has. It just contains list of ServiceDescriptor. In addition, it would be unclear in which scope service instance should be cached.
ASP.NET Core
For ASP.NET Core root IServiceProvider is app.ApplicationServices. Configure method receives first child-scope created from the root - application-scope. For every HTTP request application-scope creates child-scope which is used to resolve all services and is itself injected in controllers and views of that HTTP request. It is also used to inject all other types in a controller constructor or a view.
IFoo resolution
So, your foo from Configure method is resolved using myServiceProvider, then they both get used as input parameters for Configure. Framework does something like this:
ServiceProvider root = sc.BuildServiceProvider(validateScopes: true);
var appScope = root.CreateScope();
IFoo foo = appScope.ServiceProvider.GetService<IFoo>();
ConfigureServices(foo, appScope.ServiceProvider);
When you call sp.GetService<IFoo>() inside of Configure method it is identical to appScope.ServiceProvider.GetService<IFoo>(); that was already called from the outside. root.GetService<IFoo>() creates different IFoo instance, as it should.
More ASP.NET Core:
To prevent developers making mistake trying to resolve scoped service from the app.ApplicationServices and not realizing that it is application scoped (global), instead of being scoped to HTTP request, by default, ASP.NET Core creates root ServiceProvider using BuildServiceProvider overload:
ServiceProvider root = sc.BuildServiceProvider(validateScopes: true);
validateScopes: true to perform check verifying that scoped services never gets resolved from root provider; otherwise false.
However, this might depend on compatibility mode you're using. I'm guessing that's the reason why it allows you to resolve scoped service via app.ApplicationServices.
Update answer:
For being able to provide you with the same instance, the DI engine will need to know the current scope.
The Configure method in the Startup class is called initially by the ASP.NET Core engine, not by a request from the user which is how a scoped service is meant to be used.
Now if you try this example instead:
IServiceScope scope = app.ApplicationServices.CreateScope();
scope.ServiceProvider.GetService<IFoo>().Work();
As long as you use the same ServiceProvider from the same IServiceScope instance, you will have access to the same IFoo object and will get the same GUID.
Old answer:
A scoped service is meant to be living for a short period (scope) and get removed when they are needed anymore.
From Dependency injection in .NET
a scoped lifetime indicates that services are created once per client request (connection)
furthermore,
Do not resolve a scoped service from a singleton and be careful not to do so indirectly, for example, through a transient service.
app.ApplicationServices.GetService will try to provide you a Singleton which is not what the service is registered as.
What you see is the expected behavior. If you need the service to have the same instance for the whole period of your application's lifetime, you will have to register it as a Singleton.
Read more about the singleton lifetime and change your code from services.AddScoped<IFoo, Foo>() to services.AddSingleton<IFoo, Foo>() if you need the same value everywhere.
The IApplicationBuilder "app" is created and passed to the Configure method.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-5.0#the-configure-method
IApplicationBuilder is available to the Configure method, but it isn't registered in the service container. Hosting creates an IApplicationBuilder and passes it directly to Configure.
On the other hand the IServiceProvider "myServiceProvider" is actually activated/created and injected:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#services-injected-into-startup
Services can be injected into the Startup constructor and the Startup.Configure method.
Any service registered with the DI container can be injected into the Startup.Configure method
So we are in two different scopes, so the guid, as expected, is different.
Via ApplicationServices 27e61428-2adf-4ffa-b27a-485b9c45471d <---- created in scope "A"
Via IServiceProvider c9e86865-2eeb-44db-b625-312f92533beb <-- created in scope "B"
Via IFoo injection c9e86865-2eeb-44db-b625-312f92533beb < -- created in scope "B"
When you are in your controller each request will have it's brand new scope, a different scope than the ones discussed above:
https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#scoped
It seems to me that you expected that also the guid in the controller to be the same, you should be aware that for each request an instance of your controller is created and a DI chain starts in a fresh scope for each request.
My textbook shows an example to build identity services, below is the code:
//startup.cs
public void Configure(IApplicationBuilder app) {
app.UseStatusCodePages();
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvcWithDefaultRoute();
//try to seed an admin account for the first time the app runs
AppIdentityDbContext.CreateAdminAccount(app.ApplicationServices, Configuration).Wait();
}
//AppIdentityDbContext.cs
public class AppIdentityDbContext : IdentityDbContext<AppUser>
{
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options) : base(options) { }
public static async Task CreateAdminAccount(IServiceProvider serviceProvider, IConfiguration configuration)
{
UserManager<AppUser> userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
RoleManager<IdentityRole> roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
string username = configuration["Data:AdminUser:Name"];
string email = configuration["Data:AdminUser:Email"];
string password = configuration["Data:AdminUser:Password"];
string role = configuration["Data:AdminUser:Role"];
if (await userManager.FindByNameAsync(username) == null)
{
if (await roleManager.FindByNameAsync(role) == null)
{
await roleManager.CreateAsync(new IdentityRole(role));
}
AppUser user = new AppUser
{
UserName = username,
Email = email
};
IdentityResult result = await userManager.CreateAsync(user, password);
if (result.Succeeded)
{
await userManager.AddToRoleAsync(user, role);
}
}
}
}
and then the textbook says:
Because I am accessing a scoped service via the IApplicationBuilder.ApplicationServices provider,
I must also disable the dependency injection scope validation feature in the Program class, as shown below:
//Program.cs
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseDefaultServiceProvider(options => options.ValidateScopes = false)
.Build();
I have a basic understanding in DI, but I'm really confused with this example, below are my questions:
Q1- accessing a scoped service via the IApplicationBuilder.ApplicationServices provider
what does it mean? what services it tries to access? why it is scoped not transient or singleton?
Q2- why we have to disable the dependency injection scope validation, what does scope validation try to achieve?
In order to understand what is going on, you will first have to understand the difference between the dependency injection lifetimes:
Transient: A new instance gets created for every dependency that gets resolved.
Singleton: A single shared instance is used whenever the service gets resolved.
Scoped: A single instance is shared whenever the service gets resolved within a single scope (or request). A subsequent request will mean that a new instance will be created again.
A database context holds a connection to the database. That’s why you usually don’t want it to be a singleton, so that you don’t keep a single connection open for the whole lifetime of your application. So you would want to make it transient. But then, if you needed to access the database multiple times while serving a single request, you would be opening the database connection multiple times within a short duration. So the compromise is to make it a scoped dependency by default: That way you don’t keep the connection open for a long time, but you also can still reuse the connection for a short duration.
Now, let’s think about what happens when a singleton service depends on a non-singleton service: The singleton service gets created just once, so its dependencies are also only resolved once. That means that any dependency it has is now effectively shared throughout the lifetime of that service—which is the lifetime of the application. So by depending on non-singleton services, you effectively make those services quasi-singleton.
That’s why there is a protection in play (during development), that protects you from making this mistake: The scope validation will check that you are not depending on scoped services outside of scopes, e.g. within singleton services. That way, you are not escaping the desired lifetime of that scoped service.
When you now run AppIdentityDbContext.CreateAdminAccount within the Configure method, you are running this outside of a scope. So you are basically within “singleton land”. Any dependency you now create will be kept around. Since you resolve UserManager<AppUser> and RoleManager<IdentityRole> which both depend on the scoped database context, you are now escaping the database context’s configured scoped lifetime.
In order to fix this, you should create a short-lived scope in which you can then access scoped services (since you are within a scope) that will be properly cleaned up when the scope terminates:
public static async Task CreateAdminAccount(IServiceProvider serviceProvider, IConfiguration configuration)
{
// get service scope factory (you could also pass this instead of the service provider)
var serviceScopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
// create a scope
using (var scope = serviceScopeFactory.CreateScope())
{
// resolve the services *within that scope*
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<AppUser>>();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
// do stuff
}
// scope is terminated after the using ends, and all scoped dependencies will be cleaned up
}
So I have a console application in which I am using Autofac.
I have set up my console application as follows:
I have a class I call ContainerConfig - In this I have all my builder registrations:
public static class ContainerConfig
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.Register(c => new MatchRun()).As<MatchRun>).SingleInstance();
builder.RegisterType<AuditLogic>().As<IAuditLogic>();
builder.RegisterType<AuditRepository>().As<IAuditRepository>();
builder.RegisterType<ValidationLogic>().As<IValidationLogic>();
return builder.Build();
}
}
I call my main applcation as follows:
private static void Main(string[] args)
{
var container = ContainerConfig.Configure();
using (var scope = container.BeginLifetimeScope())
{
var app = scope.Resolve<IApplication>();
app.Run(args);
}
}
The issue is that I have a connected WCF service. This is my AuditRepository. (FYI - I have not touched WCF for years so I have forgotten most of what I knew).
Its currently constructed to create and dispose of the the proxy each time I make a call to that client. This functions - mostly.
Looks like this:
public string GetStuff(string itemA, string itemB)
{
try
{
GetProxy();
return _expNsProxy.GetStuff(itemA, itemb);
}
catch (Exception ex)
{
IMLogger.Error(ex, ex.Message);
throw ex;
}
finally
{
// CloseProxyConn();
}
}
What i am wondering is can I do this better with Autofac - creating a single instance vs the constant open close - or am I just totally crazy? I know I am not fully asking this the right way - not 100% sure how to actually word the question.
Thanks
The approach to always create a new proxy and close it after each call is good for WCF.
Otherwise you can run into issues. For example if one service call fails the channel created by the proxy goes into a faulted state and you can not do more calls on it just abort it. Then you need to create a new proxy. Also you can have threading issues if you call the same proxy from multiple threads simultaneously.
Check also this documentation with a sample of how to handle errors correctly when calling WCF services.
There is an Autofac.Wcf package that can help you with the creation and freeing of channels. Check the documentation here. It uses the dynamic client generation approach where you just give the interface of your WCF service and it generates the channel based on the interface. This is a bit more low level approach so you will have to understand more what is going on. The generated client class does this for you in the background.
You need two registrations one for the channel factory that is a singleton:
builder
.Register(c => new ChannelFactory<IYourWcfService>(
new BasicHttpBinding(), // here you will have to configure the binding correctly
new EndpointAddress("http://localhost/YourWcfService.svc")))
.SingleInstance();
And a factory registration that will create the channel from the factory every time you request the service:
builder
.Register(c => c.Resolve<ChannelFactory<IYourWcfService>>().CreateChannel())
.As<IIYourWcfService>()
.UseWcfSafeRelease();
We have several applications hosted in Windows services that self host a Nancy endpoint in order to expose instrumentation about the operation of the applications.
We use Autofac as our IOC. Several repositories are registered into the root container in a core DLL shared by all applications; this container is then passed to Nancy as its container using a bootstrapper derived from the Nancy.Autofac.Bootstrapper.
What we found was that when a web request is received by Nancy it resolves a request for a repository from the root container and this led to memory being consumed by non-garbage collected IDisposables as the root container does not go out of scope (it has the lifetime of the windows service). This led to the services "leaking" memory.
We then switched to a model where we added registrations for the repositories using InstancePerRequest in the overridden ConfigureRequestContainer() method in our Nancy bootstrapper:
protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
PerRequestContainerBuilder().Update(container.ComponentRegistry);
}
private static ContainerBuilder PerRequestContainerBuilder()
{
var builder = new ContainerBuilder();
// Dependency for repository
builder.RegisterType<SystemDateTimeProvider>().InstancePerRequest().As<IDateTimeProvider>();
// Repository
builder.RegisterType<BookmarkRepository>().InstancePerRequest().As<IBookmarkRepository>();
return builder;
}
We also override the CreateRequestContainer() method to create the request container with the tag MatchingScopeLifetimeTags.RequestLifetimeScopeTag.
protected override ILifetimeScope CreateRequestContainer(NancyContext context)
{
return ApplicationContainer.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
}
This appears to have solved the problem of IDisposables not being disposed - the child request container is disposed at the end of the web request pipeline and objects resolved by it are also disposed and eventually garbage collected.
Our problem is that this seems to be leaking the implementation details of the repositories into the services as we have to not only register the repository in ConfigureRequestContainer() but also any other objects required by the repository, i.e. if we want to change the implementation of a repository we have to "walk the dependency chain" to register required objects in each service using it - this seems wrong.
Is there a way we can get Autofac to resolve supporting objects for the repositories out of the root container but keep the registration information within the scope of the web request container? Or is there a way to automatically copy existing registrations from the root container into the child container when it is created?
Autofac should automatically resolve instances from "parent" lifetimes. If you configure your registrations using InstancePerRequest, Autofac will register these services with a special lifetime tag, MatchingScopeLifetimeTags.RequestLifetimeScopeTag, so it can be resolved in the correct scope later.
This means that there's no need to use the Nancy bootstrapper's ConfigureRequestContainer method to do request-scoped registrations. You've already done it! As long as Nancy creates the request lifetime using the same tag used in InstancePerRequest (this is done by default as of Nancy 1.1), the services should be resolved correctly.
Example:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// Do request-scoped registrations using InstancePerRequest...
var container = builder.Build();
// Pass the pre-built container to the bootstrapper
var bootstrapper = new MyAwesomeNancyBootstrapper(container);
app.UseNancy(options => options.Bootstrapper = bootstrapper);
}
}
public class MyAwesomeNancyBootstrapper : AutofacNancyBootstrapper
{
private readonly ILifetimeScope _lifetimeScope;
public MyAwesomeNancyBootstrapper(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
protected override ILifetimeScope GetApplicationContainer()
{
return _lifetimeScope; // Tell Nancy you've got a container ready to go ;)
}
}
This setup should be enough (As of Nancy 1.1. In earlier versions you have to also override the CreateRequestContainer method and pass the request lifetime tag when creating the request lifetime scope).
EDIT: I put together an example for you at https://github.com/khellang/Nancy.AutofacExample