Unity DI and custom Owin middleware - c#

Custom Owin middleware eventually needs to verify credentials against the database, yet I can't find a way to get instances of repositories or the Asp.Net Identity UserManager from the middleware via DI.
Unfortunately my database driver is a global singleton and should be resolved by the configured Unity container. When debugging, the Unity child container for the request is created after the middleware gets called.
Is there any way to resolve dependencies with Unity in the middleware?
I tried this without success, but the namespace seems to be wrong anyway (System.Web.Mvc vs System.Web.Http)
var userManager = DependencyResolver.Current.GetService<ApplicationUserManager>();
It seems Autofac solves this by creating the request scope as a middleware itself. What about Unity?
UPDATE
This works
var userManager = (ApplicationUserManager)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(ApplicationUserManager));
However the child container (BeginScope of IDependencyResolver) is created later. I registered the UserManager with a HierarchicalLifetimeManager, which probably means it resolves a single instance in the parent container and subsequent requests get the same instance.
What's safer, using a TransientLifetimeManager and just resolve in the parent container, or are there better options?

Related

Can't get AuthorizationMessageHandler in Blazor WebAssembly to work due to service lifetime issues

I cannot get an HttpClient with AuthorizationMessageHandler working.
I have a project set up using the Microsoft.AspNetCore.Components.WebAssembly.Authentication package, and wired up for OIDC. That's working fine. Now, I want to make an http call to an API in another domain, using the access token provided during the OIDC authentication.
Based on https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-5.0#typed-httpclient, this should be a simple task, but I'm having all sorts of problems with the service collection. Here's what I have to set up a typed HttpClient:
builder.Services.AddScoped<AuthorizationMessageHandler>();
builder.Services.AddHttpClient<IAuthorizationClient, AuthorizationClient>(client => client.BaseAddress = new Uri(authUrl))
.AddHttpMessageHandler(sp => sp.GetRequiredService<AuthorizationMessageHandler>().ConfigureHandler(new[] { authUrl }));
The issue here, for example, is that AuthorizationMessageHandler requires an IAccessTokenProvider in the constructor, which Blazor provides as a Scoped service. However, the HttpClientFactory is registered as a singleton service. When the HttpClientFactory tries to activate an instance of AuthorizationMessageHandler, it fails with the following error:
Cannot resolve scoped service 'Microsoft.AspNetCore.Components.WebAssembly.Authentication.AuthorizationMessageHandler' from root provider.
Registering AuthorizationMessageHandler as a Singleton, I get:
Cannot consume scoped service 'Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider' from singleton 'Microsoft.AspNetCore.Components.WebAssembly.Authentication.AuthorizationMessageHandler'.
Registering AuthorizationMessageHandler as a Transient, I get:
Cannot resolve 'Microsoft.AspNetCore.Components.WebAssembly.Authentication.AuthorizationMessageHandler' from root provider because it requires scoped service 'Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider'.
I'm out of ideas on how to get this to work. All the examples in the referenced link above don't appear to work.
I've tried this with both net5.0 and net6.0, with the same result.

How to avoid reload resource in ASP.NET core controller

I am new to ASP.net core. Having a web API connect to database using EntityFramework core. The controller take request, do some analysis, and send the response as below
public class CentoDataController : ControllerBase
{
private readonly CentoWebDBContext _context;
private HPOSubSimHolder _hpoSubSimHolder;
public CentoDataController(CentoWebDBContext context)
{
_context = context;
_hpoSubSimHolder = new HPOSubSimHolder(hpofile);
}
[HttpGet("{id}")]
public ActionResult<CentoData> GetCentoData(string id)
{
IQueryable<CentoData> r = AnalysisMethod(id, _hpoSubSimHolder);
return r;
}
The code works, but _hpoSubSimHolder will be reloaded once a new request comes in. I guess I shouldn't share controller between requests. But how can I avoid reloading _hpoSubSimHolder ?
I can see that you're using .net core dependency injection
If you want that service to be shared across requests, think of making it a Singleton.
You can choose between AddScoped, AddTransient and AddSingleton when registering dependencies.
In your startup.cs class:
public void ConfigureServices(IServiceCollection services)
{
// some code
services.AddSingleton<HPOSubSimHolder>(new HPOSubSimHolder());
}
Singleton means only a single instance will ever be created. That instance is shared between all components that require it. The same instance is thus used always.
Scoped means an instance is created once per scope. A scope is created on every request to the application, thus any components registered as Scoped will be created once per request.
Transient The services created using transient lifetime will be created each time they are requested. This lifetime works best for lightweight services.
(Source)
Controllers are always instantiated per request. To control lifetime of any resources or dependencies the controller should use, you can use the build in Dependency Injection (DI).
Most examples setup DI in your startup.cs ConfigureServices method.
The DI container allows 3 different lifetime states, in your case I guess you can try to add the HPOSubSimHolder as singleton.
I have no idea what HPOSubSimHolder is and what the implementation details are, hence its hard to tell if that'll work for you.
But it would be the "normal" way of setting this up ;)

Cannot resolve scoped service Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.IViewBufferScope from root provider

I'm trying to use in my ASP.NET Core 2.0 web app this sample RazorViewEngineEmailTemplates to create an html email body from View. But when I run it and my controller gets an ajax request, I get this error:
Cannot resolve scoped service Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.IViewBufferScope from root provider
It's probably coming from resolving dependencies in the RazorViewToStringRenderer class but I have no idea how to fix this.
ok, the problem was I used renderer from a Singleton service (EmailerService). I changed its registration to Scoped and it all works now:
services.AddScoped<IEmailer, EmailerService>();
When a service gets injected as scoped, the dependent class must also be injected as scoped.
Unfortunately, this does not work for every use case. When creating the E-Mail service statically, you don't have an HTTP Context.
In my case, I had a scheduled Task that was executed statically by Hangfire:
var mailer = ServiceProviderSinleton.Instance.GetService(typeof(IEmailer))
When you need that scoped service from a static context, you have two options:
use a dependency injection framework that gives you more control over the injection context. I highly recommend DryIoc.Microsoft.DependencyInjection from NuGet. (Documentation)
disable the scope validation:
return WebHost.CreateDefaultBuilder()
.ConfigureLogging(builder => builder.AddSerilog(Log.Logger, dispose: true))
.UseKestrel(options => options.ConfigureEndpoints(configuration))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<TStartup>()
.UseSerilog()
.UseDefaultServiceProvider(options => options.ValidateScopes = false)
.Build();

Resolving Entity Framework's AccountController with Simple Injector and Web API

There is an issue of AccountController instantiation due to the dependencies of ApplicationUserManager, UserStore in asp .net Web api. I am able to succesfully register all other dependencies for simple injector DI.
I have searched a number of places to inject the right instances/types but could not conclusively do it properly yet.
Is there a way for me to ignore the default RegisterWebApiControllers given by simple injector so that I can avoid instantiating the AccountController via simple injector? Of is it impractical?
Just get rid of the AccountController's default constructor. It is as simple as that.
Another option is to override the AccountController registration as follows:
container.RegisterWebApiControllers(config);
container.Options.AllowOverridingRegistrations = true;
container.Register<AccountController>(() => new AccountController());
container.Options.AllowOverridingRegistrations = false;
Here are some interesting reads when using Identity with Simple Injector:
https://simpleinjector.codeplex.com/discussions/564822
https://simpleinjector.codeplex.com/discussions/578859
https://github.com/simpleinjector/SimpleInjector/issues/37
https://github.com/simpleinjector/SimpleInjector/issues/93

ASP.NET MVC 5 + Owin + SimpleInjector

A new asp.net mvc project using owin, webapi, mvc and DI (SimpleInjector) runs fine if I remove the DI lib from the project. However, once introduced, the app blows up when registering the OWIN components for DI. The OWIN startup configuration is being hit and runs without error, but when it comes time to register the dependencies (listed below) I receive the following error:
An exception of type 'System.InvalidOperationException' occurred in Microsoft.Owin.Host.SystemWeb.dll but was not handled in user code
Additional information: No owin.Environment item was found in the context.
SimpleInjector Registration Code:
container.RegisterPerWebRequest<IUserStore<ApplicationUser>>(() => new UserStore<ApplicationUser>());
container.RegisterPerWebRequest<HttpContextBase>(() => new HttpContextWrapper(HttpContext.Current));
// app fails on call to line below...
container.RegisterPerWebRequest(() => container.GetInstance<HttpContextBase>().GetOwinContext());
container.RegisterPerWebRequest(() => container.GetInstance<IOwinContext>().Authentication);
container.RegisterPerWebRequest<DbContext, ApplicationDbContext>();
Update - Full Stack Trace
at
System.Web.HttpContextBaseExtensions.GetOwinContext(HttpContextBase
context) at
WebApplication1.App_Start.SimpleInjectorInitializer.<>c__DisplayClass6.b__2()
in
b:\temp\WebApplication1\WebApplication1\App_Start\SimpleInjectorInitializer.cs:line
41 at lambda_method(Closure ) at
SimpleInjector.Scope.CreateAndCacheInstance[TService,TImplementation](ScopedRegistration2
registration) at
SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration2
registration) at
SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration2
registration, Scope scope) at
SimpleInjector.Advanced.Internal.LazyScopedRegistration2.GetInstance(Scope
scope) at lambda_method(Closure ) at
SimpleInjector.InstanceProducer.GetInstance()
I think the exception is thrown when you call Verify(). Probably at that line, but only when the delegate is called.
Simple Injector allows making registrations in any order and will therefore not verify the existence and correctness of a registration’s dependencies. This verification is done the very first time an instance is requested, or can be triggered by calling .Verify() at the end of the registration process.
I suspect you're registrering the OwinContext only because you need it for getting the IAuthenticationManager.
The problem you face is that the OwinContext is only available when there is a HttpContext. This context is not available at the time the application is build in the composition root. What you need is a delegate which checks the stage of the application and returns a component that matches this stage. You could that by registering the IAuthenticationManager as:
container.RegisterPerWebRequest<IAuthenticationManager>(() =>
AdvancedExtensions.IsVerifying(container)
? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication);
The delegate will return the Owin controlled IAuthenticationManager when the code runs at 'normal runtime stage' and there is a HttpContext.
But when making an explicit call the Verify() (which is highly advisable to do!) at the end of registration process there is no HttpContext. Therefore we will create a new OwinContext during verifying the container and return the Authentication component from this newly created OwinContext. But only if the container is indeed verifying!
A full and detailed description can be read here as already mentioned in the comments.
Although the question is different, the answer is the same as my answer here.
The problem is that you are injecting HttpContextWrapper into your application and attempting to use its members during application initialization, but at that point in the application lifecycle, HttpContext is not yet available. HttpContext contains runtime state, and it does not make sense to initialize an application within one specific user's context.
To get around this problem, you should use one or more Abstract Factories to access HttpContext at runtime (when it is available) rather than at application initialization, and inject the factories into your services with DI.
Using Ric .Net's answer might work, too, but it will throw an exception every time the application is initialized.
The answer of 'Ric .Net' has pointed me in right direction, but to allow changes to new SimpleInjector, have to change the code as below (as RegisterPerWebRequest is obselete):
container.Register<IAuthenticationManager>(() => AdvancedExtensions.IsVerifying(container)
? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication, Lifestyle.Scoped);
Also, have to add below two registrations to the container, to allow 'container.Verify()' to work correctly:
container.Register<ApplicationUserManager>(Lifestyle.Scoped);
container.Register<ApplicationSignInManager>(Lifestyle.Scoped);

Categories