I have the following basic SI registration in an ASP.NET WebApi project.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSimpleInjector(container, options =>
{
options
.AddAspNetCore()
.AddControllerActivation();
});
services.AddHttpContextAccessor();
services.AddScoped<Work>(services =>
{
var traceId = services.GetRequiredService<IHttpContextAccessor>().HttpContext.TraceIdentifier;
// ...
});
}
public void Configure(IApplicationBuilder app)
{
app.ApplicationServices.UseSimpleInjector(container);
// ...
container.Verify();
}
private readonly Container container = new Container();
The Problem
Container.Verify() attempts to resolve a Work instance, whose factory delegate successfully resolves an IHttpContextAccessor but its HttpContext is null because there is no current HTTP call on startup. Therefore the code dies with a null-reference exception.
I don't think there is anything we can do except guard against null but that goes against my taste in this context:
why would I do that when I know for a fact that this factory delegate should only be called during an HTTP call?
what exactly do I do if my HTTP-scoped dependency is null? Sure, return a fake BUT how do I detect that it's null for good reason and not because my web infrastructure is dying somehow?
I can't see a good solution. What do you do in this case?
I've wrote extensively about this problem in the past. Your HttpContext and its TraceIdentifier are runtime data and runtime data should be kept out of the construction of your object graphs. See for instance this article.
What the correct solution in your particular case is, is hard to say, because it depends on the details of what you are trying to construct, but the article gives some pointers.
Related
I register a service FooRequest as InstancePerRequest in ASP.NET MVC & OWIN:
builder.RegisterType<FooRequest>().AsSelf().InstancePerRequest();
After that I resolve FooRequest in two locations. First is global.asax Application_BeginRequest():
protected void Application_BeginRequest(object sender, EventArgs e)
{
var fooRequest = DependencyResolver.Current.GetService<FooRequest>();
}
A second time in another services constructer. The other service has InstancePerLifetimeScope:
public class FooService
{
public FooService(FooRequest fooRequest)
{
...
}
}
My problem is that those two resolves in different instances of FooService and the one used in constructor injection of service does not call Dispose[Async] on the end of the request.
What am I doing wrong?
Btw. using DependencyResolver.Current.GetService<FooRequest>() outside the constructor does resolve the proper instance of FooRequest in FooService.
Additional requested information
OWIN & Container configuration:
[assembly: OwinStartup(typeof(Project.Web.Startup))]
namespace Project.Web
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// REGISTER CONTROLLERS SO DEPENDENCIES ARE CONSTRUCTOR INJECTED
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterApiControllers(typeof(MvcApplication).Assembly);
// REGISTER DEPENDENCIES
builder.RegisterModule(new ProjectWebModule());
builder.Register(c => new IdentityFactoryOptions<ApplicationUserManager> { DataProtectionProvider = app.GetDataProtectionProvider() });
// REGISTER FILTERS SO DEPENDENCIES ARE PROPERTY INJECTED
builder.RegisterFilterProvider();
// BUILD THE CONTAINER
var container = builder.Build();
// REPLACE THE MVC DEPENDENCY RESOLVER WITH AUTOFAC
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// REPLACE THE WEBAPI DEPENDENCY RESOLVER WITH AUTOFAC
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// REGISTER WITH OWIN
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
app.Use((context, next) =>
{
var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
httpContext.SetSessionStateBehavior(SessionStateBehavior.Required);
return next();
});
// STANDARD MVC SETUP
RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); // immer nach RouteConfig.RegisterRoutes ausführen!
BundleConfig.RegisterBundles(BundleTable.Bundles);
// PLACE ConfigureAuth AFTER RegisterGlobalFilters
ConfigureAuth(app);
}
}
}
ProjectWebModule
namespace Project.Web.Autofac
{
public class ProjectWebModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<FooRequest>().AsSelf().InstancePerRequest();
builder.RegisterType<FooService>().AsSelf().InstancePerLifetimeScope();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>();
builder.Register(c =>
//register FakeHttpContext when HttpContext is not available
HttpContext.Current != null
? new HttpContextWrapper(HttpContext.Current) as HttpContextBase
: new FakeHttpContext("~/") as HttpContextBase)
.As<HttpContextBase>()
.InstancePerLifetimeScope()
.OnActivated(e =>
{
Toolbox.HttpContext = e.Instance;
});
builder.Register(c => c.Resolve<HttpContextBase>().Request)
.As<HttpRequestBase>()
.InstancePerLifetimeScope();
builder.Register(c => c.Resolve<HttpContextBase>().Response)
.As<HttpResponseBase>()
.InstancePerLifetimeScope();
builder.Register(c => c.Resolve<HttpContextBase>().Server)
.As<HttpServerUtilityBase>()
.InstancePerLifetimeScope();
builder.Register(c => c.Resolve<HttpContextBase>().Session)
.As<HttpSessionStateBase>()
.InstancePerLifetimeScope();
base.Load(builder);
}
}
}
I mention in the comments on the question that there's not enough info here to really tell, but here's my guess: you have a race condition.
As you know from the Autofac.Mvc.Owin README and the MVC/OWIN integration docs, ASP.NET MVC doesn't actually fully run in the OWIN pipeline. Trying to make the two work creates some weird issues that aren't of Autofac's creation. For example (again, from the Autofac docs):
Minor gotcha: MVC doesn’t run 100% in the OWIN pipeline. It still needs HttpContext.Current and some other non-OWIN things. At application startup, when MVC registers routes, it instantiates an IControllerFactory that ends up creating two request lifetime scopes. It only happens during app startup at route registration time, not once requests start getting handled, but it’s something to be aware of. This is an artifact of the two pipelines being mangled together. We looked into ways to try working around it but were unable to do so in a clean fashion.
But that also means there's a sort of order around when Application_BeginRequest handlers run that could cause oddness like what you're seeing. For example, the Autofac OWIN MVC integration tries to set up a request lifetime (if it's not already set up) when you put app.UseAutofacMvc() in the pipeline, but the ASP.NET MVC framework also tries to set up a request lifetime internally (using DependencyResolver.Current), so those things have to work together. It's not entirely impossible that your Application_BeginRequest handler is resolving from a different lifetime scope than what the MVC framework is trying to set up as the request lifetime, for example if the MVC framework hasn't had a chance to set it up before you've tried resolving from it.
I would recommend if you're trying to use OWIN with MVC, give in to OWIN and actually use OWIN middleware in the pipeline rather than event handlers in MVC. It'll remove the race condition for Application_BeginRequest and give you greater control over the order of operations. It'll also get you closer to where ASP.NET Core is so if/when it's time to migrate your application you won't have to deal with the event handlers that aren't there anymore.
Of course, again, this is totally a guess based on what I could gather from the question. Hopefully it helps.
Edit after new info was added to question: I think my guess is still correct, but it also looks like you're not setting up OWIN right for WebAPI, which could contribute to the problem. You shouldn't be using GlobalConfiguration. Also, again, MVC doesn't really run in OWIN, so you may see weirdness trying to get two app types (MVC, WebAPI) with two different pipelines to mash together. It's why they unified it in ASP.NET Core.
I have the following code in the Startup code of one project:
public void ConfigureServices(IServiceCollection services)
{
...
services
.AddAppCommonServices(configuration)
.AddSingleton<IKeyVaultClient, KeyVaultClient>();
IKeyVaultClient keyVaultClient = services.BuildServiceProvider().GetService<IKeyVaultClient>();
services.RegisterMonitoringTelemetry(keyVaultClient.GetSecretAsync(configuration[Consts.AppInsightsInstrumentationKey]).Result);
...
}
This is working fine. But since we recently updated the .NET Core version, we started getting a warning message due to the call to the BuildServiceProvider() method. I was looking for how to properly write the code above to avoid the problem described by the warning and I understand that the right way is to use the Options Pattern to inject the necessary values. However, while I understand the concept, I'm still confused on how to actually use it in my case.
I tried this:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddOptions<KeyVaultClientSettings>()
.Configure<IKeyVaultClient>((settings, kvc) =>
{
settings.Secret = kvc.GetSecretAsync(configuration[Consts.AppInsightsInstrumentationKey]).Result;
}
);
services
.AddAppCommonServices(configuration)
.AddSingleton<IKeyVaultClient, KeyVaultClient>();
services
.Configure<KeyVaultClientSettings>(settings =>
{
services.RegisterMonitoringTelemetry(settings.Secret);
});
...
}
But it doesn't work. From debugging, it seems that the RegisterMonitoringTelemetry method is never called, though I'm not sure why.
Any help on how to fix my code, I would be super thankful!
Try this way. I think the issue is because you didn't create a scope. However even this solution is not the best practice.
Calling async method in ConfigureServices with .Result is also kind of bad stuff.
using var scope = services.BuildServiceProvider().CreateScope();
var ketVaultClient = scope.ServiceProvider.GetRequiredService<IKeyVaultClient>();
services.RegisterMonitoringTelemetry(keyVaultClient.GetSecretAsync(configuration[Consts.AppInsightsInstrumentationKey]).Result);
I have an issue with a database context being disposed. I have set up the databases like the below in the Configure services method. The code has been simplified to hopefully make it easier to read.
public void ConfigureServices(IServicesCollection services)
{
Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database1")));
Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database2")));
Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database3")));
Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database4")));
Services.AddScoped<IQueryService1, ConcreteService1>();
Services.AddScoped<IQueryService1, ConcreteService2>();
Services.AddScoped<IQueryService1, ConcreteService3>();
Services.AddScoped<IQueryService1, ConcreteService4>();
}
Now in one of the controllers I inject the relevant services that are required.
[Produces("application/json")]
[Route("api/[controller]/[action]
public class DigitalFinesController : Controller
{
private readonly IQueryService1 _Service1;
public DigitalFinesController(IConfiguration configuration, IQueryServices1 QueryService1)
{
_Service1 = QueryService1;
}
[Authorize]
[HttpPost]
[ActionName("SubmitFine")]
[ProducesResponseType(200)]
[ProducesResponseType(401)]
public async Task<IActionResult> SubmitFine([FromBody] Models.DigitalFine fine)
{
//This is a simple version of my issue
var vehicles = _Service1.Vehicles.FirstOrDefault(p=> p.vrm == "OX7 DFG");
if(vehicle == null)
{
return BadRequest("Vehicle is missing");
}
var fleet = _Service1.Fleets.FirstOrDefault(p=> p.Code = "MyCode");
}
}
And once I get to the second query I get the following exception
System.ObjectDisposedException: 'Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
And I am stumped to understand why this is happening. Can anyone please give me a pointer to fix this?
Many thanks
Simon
I think it may have to do with how you're registering it. Try registering it with AddSingleton instead of AddScoped
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2#service-lifetimes-and-registration-options
Scoped
Scoped lifetime services are created once per request.
Warning
When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, see ASP.NET Core Middleware.
Singleton
Singleton lifetime services are created the first time they're requested (or when ConfigureServices is run and an instance is specified with the service registration). Every subsequent request uses the same instance. If the app requires singleton behaviour, allowing the service container to manage the service's lifetime is recommended. Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.
Warning
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
.AddScoped will dispose after lifetime of the request, try changing to singleton or transient:
Please see documentation:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2
At this section:
In the sample app, the IMyDependency service is registered with the
concrete type MyDependency. The registration scopes the service
lifetime to the lifetime of a single request. Service lifetimes are
described later in this topic.
Warning
When using a scoped service in a middleware, inject the service into
the Invoke or InvokeAsync method. Don't inject via constructor
injection because it forces the service to behave like a singleton.
For more information, see ASP.NET Core Middleware.
I'm trying to configure my services for an ASP.NET Core 2.0 app/website.
I wish to reference some key/values from my appsettings.json file, in this method.
I'm not sure if what I'm going is OK or not:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters()
.AddCors();
var applicationSettings = Configuration.GetSection("Settings").Get<ApplicationSettings>();
services.AddSingleton(applicationSettings);
// ** THIS IS WHAT I ORIGINALLY HAD, BUT IT'S ONLY SETTING
// THE VALUE IN DI/IOC.
//services.Configure<ApplicationSettings>(options => Configuration.GetSection("Settings")
// .Bind(options));
var foo = new Foo(applicationSettings.SomeSetting);
services.AddSingleton(foo);
}
See how i'm manually adding a singleton and then later, referring a value from the app settings instance?
vs
just configuring ...
So, is either way OK or is there a specific reason for either/or?
Remember -> i'm going to need to inject my settings into controllers, etc...
Technically, you can do either. In both cases, you have the configuration registered and available through dependency injection, so everything can depend on it and will get the configuration instance.
You are also using the centrally set up Configuration there, so you have all the benefits from the configuration stack there, e.g. multiple providers or environment specific overrides.
However, the favor has definitely moved to the IOptions way of consuming custom configuration. It’s the “state of the art” and used throughout ASP.NET Core for literally everything. It also allows you to switch to options that can be updated at runtime. That’s very powerful and might become useful eventually (not necessarily for your specific situation with the singleton, but maybe for something else).
It’s also really easy to set this up, actually shorter than what you tried:
services.Configure<ApplicationSettings>(Configuration.GetSection("Settings"));
services.AddSingleton<Foo>();
Note that, even for singletons, you shouldn’t explicitly create a new instance of it, but let DI handle that. If your class has the correct constructor, dependencies will be automatically injected anyway:
public class Foo
{
private readonly ApplicationSettings _settings;
public Foo (IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
}
Of course, Foo can also have more dependencies here. Since it’s going to be constructed by DI, you can just add more dependencies in the constructor, without having to update some new call somewhere.
If you need to configure certain services with settings that depend on your configuration, you still should not bind your configuration there directly. All of configuration is DI-based, so you just need to inject the right thing; a IConfigureOptions<T>. That’s basically the thing that provides the IOptions<T> to services later. In your JWT case, this could look like this:
// instead of passing an option configuration delegate here…
services.AddAuthentication().AddJwtBearer();
// … we register a IConfigureOptions<JwtBearerOptions> instead
services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureJwtBearerOptions>();
// … ConfigureJwtBearerOptions could look like this:
class ConfigureJwtBearerOptions : IConfigureOptions<JwtBearerOptions>
{
private readonly ApplicationSettings _settings;
public ConfigureJwtBearerOptions(IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
public void Configure(JwtBearerOptions options)
{
// configure JwtBearerOptions here, and use your ApplicationSettings
options.MetadataAddress = _settings.JwtMetadataAddress;
}
}
This might seem unnecessarily verbose compared to just passing a delegate to AddJwtBearer() but note that this is exactly what happens under the hood when you pass that delegate: An IConfigureOptions<JwtBearerOptions> object will be created that calls your delegate in the Configure() call. So this is really just the same.
Note that for authentication schemes, you might actually set up a IConfigureNamedOptions<T> instead, which is almost the same thing except it can configure the options based on a name. For authentication schemes, that is the scheme name, so basically you check the scheme name in Configure() and then decide how to configure your options.
As for creating singleton instances, especially expensive ones, I would argue that ConfigureServices is the wrong place for such a thing. ConfigureServices is called very early in the application startup phase, when the whole DI infrastructure does not exist yet. So you could not rely on anything when creating your instance. I would also argue that it is still not your job to create the object but you should DI handle the creation of it and as such give it also control over its lifecycle.
If you absolutely need to control when the instance is created, I would suggest you to use the lifecycle events for this: Basically, after the application has set up properly but before a first request comes in, you request the instance of your services and initialize it. That way, you can still have it fully depend on DI, and it won’t be created lazily with the first request.
You can register lifecycle handlers in the Configure method:
public void Configure(IApplicationBuilder app, IApplicationLifetime applicationLifetime)
{
applicationLifetime.ApplicationStarted.Register(() =>
{
// application has started, request the singleton here to trigger DI to
// create the instance
app.ApplicationServices.GetService<ExpensiveSingleton>();
});
// …
}
}
Well the problem with that approach is that it will be impossible to load multiple configuration sections through DI. The Configuration API has many features, such as pluggable configuration provides, snapshots, etc.
I would suggest you at least use a class to bind you configuration section against, so DI can inject it based on its type. If you further down the line have need to another configuration class you won't run into issues.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration
I'm trying to implement a new extension method for the IWebHostBuilder that executes some code, like registering somewhere.
The important part is, that it also unregisters when the application is shutting down.
I added some code which shows what I want to do. The problem here is, that the instance of IApplicationLifetime is not available yet. This new extension method was added last in the pipeline of building the WebHost.
public static IWebHostBuilder UseHttpGatewayappRegistration(this IWebHostBuilder webHostBuilder)
{
webHostBuilder.ConfigureServices(services =>
{
var sp = services.BuildServiceProvider();
// This instance is not available
IApplicationLifetime applicationLifetime = sp.GetService<IApplicationLifetime>();
// These instances are ok
AppSettings appSettings = sp.GetService<AppSettings>();
ILoggerFactory loggerFactory = sp.GetService<ILoggerFactory>();
var registration = new Registration(loggerFactory.CreateLogger<Registration>());
applicationLifetime.ApplicationStopping.Register(() =>
{
registration.Unregister();
});
});
return webHostBuilder;
}
Why is the IApplicationLifetime instance null, even though I added this extension method last in the pipeline of building the WebHost pipeline? It would be great, if someone would provide me with some information about the execution order of all "ConfigureServices" methods and how or if it is at all possible to use IApplicationLifetime in a ConfigureServices method.
I know I could do this all without the WebHostBuilder, but it seems logical to me to do it there and I also think there has to be a way.
Unfortunately I couldn't find much information online...
Thank you.
EDIT: I found a way to use DI in ConfigureServices methods. I edited the question accordingly. This helped me: How to Resolve Instance Inside ConfigureServices in ASP.NET Core
You can't get access to IApplicationLifetime instance from ConfigureServices method. It's by design. Check the "Services Available in Startup" section here:
Application Startup in ASP.NET Core.
Resolve IApplicationLifetime in the IWebHostBuilder.Configure method (it replaces the Startup.Configure method if it is used later in IWebHostBuilder configuration pipeline):
public static IWebHostBuilder Foo(this IWebHostBuilder webHostBuilder)
{
webHostBuilder.Configure(x =>
{
var service = x.ApplicationServices.GetService<IApplicationLifetime>();
});
return webHostBuilder;
}
It's also possible to extend the original Startup.Configure method instead of replace (a lot of Reflection logic should be added to make this solution reliable):
.Configure(app =>
{
var env = app.ApplicationServices.GetService<IHostingEnvironment>();
dynamic startup = Activator.CreateInstance(typeof(TStartup), env);
//Carefully resolve all input parameters.
//Make sure all required services are registered in DI.
startup.Configure(app, env);
var lifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
})