Resolve Autofac service within InstancePerLifetimeScope on Owin Startup - c#

I cannot figure out a correct way to resolve a service via Autofac that is used while constructing the Owin context and also disposed at the request end.
Since the OwinContext is still under construction at this point, the LifetimeScope cannot be found by calling HttpContext.Current.GetOwinContext().GetAutofacLifetimeScope(). The OwinContext is not available here yet.
In my code the IAdfsAuthorizationProvider service is resolved directly at the Container, but isn't disposed after a request and lives much longer.
I could create a new LifeTimeScope by calling container.BeginLifetimeScope(), but now the LifeTime is separated of the request and will possibly resolve different instances in the same request. And there is no way to dispose the lifeTimeScope myself at the correct time.
public void Configure(IAppBuilder app)
{
var builder = new ContainerBuilder();
builder.RegisterType<AdfsAuthorizationProvider>().As<IAdfsAuthorizationProvider>().InstancePerLifetimeScope();
var container = builder.Build();
app.UseAutofacMiddleware(container);
// **Service that's not binding to the request lifetime.**
var service = container.Resolve<IAdfsAuthorizationProvider>();
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
Provider = new AuthorizationServerProvider(service),
});
}
Does anyone has a suggestion?

The OAuthAuthorizationServerProvider is a singleton that call methods for all requests.
This class has not been designed to be injected. If you want to resolve a dependency for one request you will have to resolve it manually using the owinContext provided with each method
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task GrantAuthorizationCode(
OAuthGrantAuthorizationCodeContext context)
{
IAdfsAuthorizationProvider authorizationProvider =
context.OwinContext
.GetAutofacLifetimeScope()
.Resolve<IAdfsAuthorizationProvider>();
return base.GrantAuthorizationCode(context);
}
}

Related

Resolving Hangfire dependencies/HttpContext in .NET Core Startup

I've installed and configured Hangfire in my .NET Core web application's Startup class as follows (with a lot of the non-Hangfire code removed):
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseHangfireServer();
//app.UseHangfireDashboard();
//RecurringJob.AddOrUpdate(() => DailyJob(), Cron.Daily);
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<AppSettings>(Configuration);
services.AddSingleton<IConfiguration>(Configuration);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IPrincipal>((sp) => sp.GetService<IHttpContextAccessor>().HttpContext.User);
services.AddScoped<IScheduledTaskService, ScheduledTaskService>();
services.AddHangfire(x => x.UseSqlServerStorage(connectionString));
this.ApplicationContainer = getWebAppContainer(services);
return new AutofacServiceProvider(this.ApplicationContainer);
}
}
public interface IScheduledTaskService
{
void OverduePlasmidOrdersTask();
}
public class ScheduledTaskService : IScheduledTaskService
{
public void DailyJob()
{
var container = getJobContainer();
using (var scope = container.BeginLifetimeScope())
{
IScheduledTaskManager scheduledTaskManager = scope.Resolve<IScheduledTaskManager>();
scheduledTaskManager.ProcessDailyJob();
}
}
private IContainer getJobContainer()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new BusinessBindingsModule());
builder.RegisterModule(new DataAccessBindingsModule());
return builder.Build();
}
}
As you can see, I'm using Autofac for DI. I've set things up to inject a new container each time the Hangfire job executes.
Currently, I have UseHangfireDashboard() as well as the call to add my recurring job commented out and I'm receiving the following error on the line referencing IPrincipal:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
I understand that Hangfire does not have an HttpContext. I'm not really sure why it's even firing that line of code for the Hangfire thread. I'm ultimately going to need to resolve a service account for my IPrincipal dependency.
How can I address my issue with Hangfire and HttpContext?
The main problem I'm having now is when I add UseHangfireServer, I
then need to resolve HttpContext too
Found here Using IoC containers
HttpContext is not available
Request information is not available during the instantiation of a
target type. If you register your dependencies in a request scope
(InstancePerHttpRequest in Autofac, InRequestScope in Ninject and so
on), an exception will be thrown during the job activation process.
So, the entire dependency graph should be available. Either register
additional services without using the request scope, or use separate
instance of container if your IoC container does not support
dependency registrations for multiple scopes.
resolving scoped dependencies in .net core would require a request which is not available during startup when registering and activating jobs. Therefore make sure that your service required for activation during startup are not registered using scoped lifetimes.
services.AddTransient<IScheduledTaskManager, ScheduledTaskManageImplementation>();
All that is left now is to configure the application to use that service with the recurring job,
public class Startup {
public IContainer ApplicationContainer { get; private set; }
public Startup(IHostingEnvironment env) {
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void Configuration(IApplicationBuilder app) {
// app.AddLogger...
//add hangfire features
app.UseHangfireServer();
app.UseHangfireDashboard();
//Add the recurring job
RecurringJob.AddOrUpdate<IScheduledTaskManager>(task => task.ProcessDailyJob(), Cron.Daily);
//app.UseMvc...
//...other code
}
public IServiceProvider ConfigureServices(IServiceCollection services) {
// Adding custom services
services.AddTransient<IScheduledTaskManager, ScheduledTaskManageImplementation>();
//add other dependencies...
// add hangfire services
services.AddHangfire(x => x.UseSqlServerStorage("<connection string>"));
//configure Autofac
this.ApplicationContainer = getWebAppContainer(services);
//get service provider
return new AutofacServiceProvider(this.ApplicationContainer);
}
IContainer getWebAppContainer(IServiceCollection service) {
var builder = new ContainerBuilder();
builder.RegisterModule(new BusinessBindingsModule());
builder.RegisterModule(new DataAccessBindingsModule());
builder.Populate(services);
return builder.Build();
}
//...other code
}
References
Hangfire 1.6.0
Integrate HangFire With ASP.NET Core
Using IoC containers
Why is Hangfire trying to resolve the .NET Core Startup class?
Hangfire doesn't store lambda expressions in the database, it stores the type and method being called. Then when the scheduled task is due to run, it resolves the type from the container and calls the method.
In your case, the method is on Startup.
You can register Startup with Autofac if you want, but it's probably easiest to have a scheduled task service:
AddOrUpdate<IScheduledTaskService>(x => x.DailyTask(), Cron.Daily);
I'm not sure of the type for jobmanager off the top of my head, but you can resolve the dependency from the container using a scope. You'll want to resolve from the scope in a using statement to prevent memory leaks. See the Autofac Docs
// not sure what type "jobManager" is
TYPE jobManager;
using(var scope = ApplicationContainer.BeginLifetimeScope())
{
jobManager = scope.Resolve<TYPE>();
}
RecurringJob.AddOrUpdate( ... );

Instanciate new AuthorizationServerProvider when per http request using dependency injection

I have a web api 2 project and I am using Asp.net Identity and Entity framework.
I have discovered that when I change the password, the old one still works but the new one give me error (invalid grant).
I am sure the change password works correctly... Infact in the database the value of the (hashed) password changes correctly.
UPDATE
I have understood why. The authorizationServerProvider (or the user manager I use inside it) is not instanciated per http request. It seems it is a singleton. The problem is that it is instanciated on startup and stop, so it's like it continue to use data of the first login, that are cached (by Entity framework).
Here how I configure my authorizationServerProvider class:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
OAuthAuthorizationServerOptions oauthServerOptions = new OAuthAuthorizationServerOptions
{
//...,
Provider = new TokenBasedAuthorizationServerProvider(),
RefreshTokenProvider = new RefreshTokenProvider()
};
app.UseOAuthAuthorizationServer(oauthServerOptions);
}
How can avoid the login use the cached data?
Thank you
You can try to:
Disable Tracking using AsNoTracking()
Throw away the DbContext and create a new one
Call GetDatabaseValues() to get updated values for single entity
I had the same problem with UseOAuthAuthorizationServer and DI. Last time I faced with this when using Autofac but this is relevant to other DI frameworks. The problem is we have static instance of OAuthAuthorizationServerProvider stored by middleware so no DI involved.
My solution was to get dependencies in OAuthAuthorizationServerProvider in each methods like this:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var scope = context.OwinContext.GetAutofacLifetimeScope();
var userManager = scope.Resolve<ApplicationUserManager>();
.........
}
My container initialization:
public static IContainer Configure(IAppBuilder appBuilder, HttpConfiguration config)
{
var containerBuilder = new ContainerBuilder();
.... registrations here ....
var container = containerBuilder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
appBuilder.UseAutofacMiddleware(container);
appBuilder.UseAutofacWebApi(config);
return container;
}
You can do something like this using other DI containers as well.

How to inject a service dependent on IOwinContext into custom OAuthAuthorizationServerProvider

Having an issue injecting IOwinContext into my OAuthAuthorizationServerProvider.
Inside my Startup.cs i have the line:
public static IContainer ConfigureContainer(Type mvcApplication, HttpConfiguration configuration = null)
{
var builder = new ContainerBuilder();
//other stuff
builder
.Register(ctx=>HttpContext.Current.GetOwinContext())
.As<IOwinContext>();
//other stuff
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(configuration);
app.UseWebApi(configuration);
}
In my provider I do:
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider{
public ApplicationOAuthProvider(IComponentContext context)
{
_ccontext = context;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var logger = _ccontext.Resolve<EventLoggerService>();
}
}
The line above crashes because I have an injectable that needs IOwinContext. The error is:
: Autofac.Core.DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Assistant.Framework.Services.CurrentUserService' can be invoked with the available services and parameters:
Cannot resolve parameter 'Microsoft.Owin.IOwinContext context' of constructor 'Void .ctor(Microsoft.Owin.IOwinContext)'.
Short version
You don't need to register IOwinContext in the container yourself if you use Autofac and Web API integration using OWIN.
The integration package Autofac.WebApi2.Owin does it for you. All you have to do is inject IOwinContext wherever you want it to be injected and it will work out of the box, as you can see on this repo on GitHub
Longer version, a.k.a. "how does this happen?"
The reason is that when using the OWIN integration package for Autofac, the IOwinContext is automatically registered in every per-request lifetime scope. The magic happens when you call app.UseAutofac(container) in this file, and here's an excerpt of the code:
private static IAppBuilder RegisterAutofacLifetimeScopeInjector(this IAppBuilder app, ILifetimeScope container)
{
app.Use(async (context, next) =>
{
using (var lifetimeScope = container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag,
b => b.RegisterInstance(context).As<IOwinContext>()))
{
context.Set(Constants.OwinLifetimeScopeKey, lifetimeScope);
await next();
}
});
app.Properties[InjectorRegisteredKey] = true;
return app;
}
An anonymous middleware is registered in the OWIN pipeline, which does 3 things:
Creates a new lifetime scope for the current HTTP request
Registers the current IOwinContext in that new lifetime scope
Stores the current lifetime scope in the IOwinContext
All this means that the lifetime scope that resolves your services in your Web API application already knows how to inject IOwinService, so no additional work is required on your side.

Getting the User.Identity during service Resolution in Autofac and WebAPI 2 Owin

What I', trying to do is have a UserContext instantiated (single instance per user ideally). The UserContext depends on the current logged-in user.
MyUser is created by calling GlobalContext.User(username)
The following snippet is called from my Startup.Configuration(IAppBuilder app):
private static IContainer RegisterServices()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(System.Reflection.Assembly.GetAssembly(typeof(MyWeb.API.Startup)));
builder.RegisterType<GlobalContext>().As<IGlobalContext>().SingleInstance();
builder.Register(c => c.Resolve<IGlobalContext>().User(
HttpContext.Current.User.Identity.Name)
).As<MyUser>();
builder.RegisterType<UserContext>().As<IUserContext>().InstancePerLifetimeScope();
return builder.Build();
}
Everything seems to work fine when ran in IIS Express.
When I run this in an integration test using the Owin WebApp.Start<Startup>(url: BaseAddress) HttpContext.Current is null. This is expected from all the reading that I've done so far, but what's the alternative?
I think I need to access the owinContext or the autofac lifetime scope to get the current user during resolution, but how do I do that?
Is there a better way to do this? or am I missing something trivial?
All help is appreciated.
EDITED TO ADD INFO:
In case it helps I'm including my Configuration() method here:
using System.Web;
using System.Web.Http;
using Autofac;
using Autofac.Integration.WebApi;
using Owin;
using System.Reflection;
namespace SomeNameSpace
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var configuration = new HttpConfiguration{ IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always };
WebApiConfig.Register(configuration);
BreezeConfig.Register(configuration);
CorsConfig.Register(configuration);
// Autofac IoC
var container = RegisterServices();
app.UseAutofacMiddleware(container);
app.UseWindowsAuthentication();
var urt = new UserRolesTranformer(new GlobalContext());
app.UseClaimsTranformation(urt.Transformation);
app.UseAutofacWebApi(configuration);
configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
//app.Use(typeof(TestMiddleware));
app.UseAutofacWebApi(configuration);
app.UseWebApi(configuration);
}
}
Ultimate Goal:
create a test that simulates multiple concurrent users logging in and ensuring that each user get's their own UserContext instance.
In the case of a Unit Test, you shouldn't really need access to the HttpContext or the OwinContext. As I imagine you have already discovered, a Unit Test has no concept of a HttpContext.
I am assuming that your IGlobalContext has a method User which takes a Username and returns you a User, perhaps from your database based on the Username passed in from the Current Context?
What you should do is have different Autofac registrations for your website and for your unit test so that you can have different dependencies in each environment. Then in your Unit Tests Autofac registration you could register either a completely different dependency to be resolved in your Unit Test, or you could pass into your User method, a mocked username that you know exists.
So you would keep this as your Autofac Registrations in your Website.
private static IContainer RegisterServices()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(System.Reflection.Assembly.GetAssembly(typeof(MyWeb.API.Startup)));
builder.RegisterType<GlobalContext>().As<IGlobalContext>().SingleInstance();
builder.Register(c => c.Resolve<IGlobalContext>().User(
HttpContext.Current.User.Identity.Name)
).As<MyUser>();
builder.RegisterType<UserContext>().As<IUserContext>().InstancePerLifetimeScope();
return builder.Build();
}
Then in your Unit Test, when you register your Autofac registrations with something like this..
private static IContainer RegisterServices()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(System.Reflection.Assembly.GetAssembly(typeof(MyWeb.API.Startup)));
builder.RegisterType<GlobalContext>().As<IGlobalContext>().SingleInstance();
builder.Register(c => c.Resolve<IGlobalContext>().User(
"MyTestUser")
).As<MyUser>();
builder.RegisterType<UserContext>().As<IUserContext>().InstancePerLifetimeScope();
return builder.Build();
}
This way you know in your Unit Test, exactly what User you are passing in and what User you should be getting back.
Or by using your own entirely mocked user like this ...
public class TestUser : MyUser
{
//Known Test Scenario Properties and Methods as required
}
private static IContainer RegisterServices()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(System.Reflection.Assembly.GetAssembly(typeof(MyWeb.API.Startup)));
builder.RegisterType<GlobalContext>().As<IGlobalContext>().SingleInstance();
builder.RegisterType<TestUser>().As<MyUser>();
builder.RegisterType<UserContext>().As<IUserContext>().InstancePerLifetimeScope();
return builder.Build();
}
I hope this helps?

using IOC container with IdentityFactory Middleware

I am trying to understand the UserManagerFactory middleware explained here per request lifetime management for usermanager.
I created this class which I am calling from the Startup Configuration method
public class CustomUserManagerProvider
{
public static CustomUserStore<CustomUser> CreateCustomUserStore()
{
return new CustomUserStore<CustomUser>(/*Need to inject dependencies here*/);
}
public static CustomUserManager CreateCustomUserManager(IdentityFactoryOptions<CustomUserManager> options,
IOwinContext context)
{
return new CustomUserManager(context.Get<CustomUserStore<CustomUser>>());
}
}
And the Startup Configuration
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(CustomUserManagerProvider.CreateCustomUserStore);
app.CreatePerOwinContext<IngramUserManager>(CustomUserManagerProvider.CreateCustomUserManager);
////....Other things
}
Now, My CustomUserStore has some dependencies which I want to inject in the constructor.
The composition root of the IOC container knows how to resolve these dependencies.
How do I make the CustomUserManagerProvider DI container aware(If that makes sense)...
Although this works
public static CustomUserStore<CustomUser> CreateCustomUserStore()
{
var dependency = DependencyResolver.Current.GetService<ISomeDependency>();
return new CustomUserStore<CustomUser>(dependency);
}
But, I was trying to avoid the service locator (anti)pattern. Is this my only option, Is this even right??
I am using Ninject.
Cant I just create a UserManager in requestScope in the composition root and inject into the controllers, wont that be the same thing?
In a web app, is CreatePerOwinContext same as creating InRequestScope?
Yes, you can inject the UserManager into your controllers a different way if you like, there's nothing that requires using the CreatePerOwinContext/IdentityFactoryMiddleware.

Categories