Register IAuthenticationManager with Simple Injector
I am having a configuration setup for Simple Injector where I have moved all of my registrations to OWIN pipeline.
Now the problem is I have a controller AccountController
which actually takes parameters as
public AccountController(
AngularAppUserManager userManager,
AngularAppSignInManager signinManager,
IAuthenticationManager authenticationManager)
{
this._userManager = userManager;
this._signInManager = signinManager;
this._authenticationManager = authenticationManager;
}
Now my Owin Pipeline configurations looks something like this
public void Configure(IAppBuilder app)
{
_container = new Container();
ConfigureOwinSecurity(app);
ConfigureWebApi(app);
ConfigureSimpleinjector(_container);
app.Use(async (context, next) =>
{
_container.Register<IOwinContext>(() => context);
await next();
});
_container.Register<IAuthenticationManager>(
() => _container.GetInstance<IOwinContext>().Authentication);
_container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
}
private static void ConfigureOwinSecurity(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = "AppNgCookie",
//LoginPath = new PathString("/Account/Login")
});
}
private static void ConfigureWebApi(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
private static void ConfigureSimpleinjector(Container container)
{
SimpleInjectorInitializer.Initialize(container);
}
And Simple Injector Initializer looks something like this
private static void InitializeContainer(Container container)
{
container.Register<DbContext, AngularAppContext>();
container.Register<IUserStore<Users, Guid>, AngularAppUserStore>();
container.Register<IRoleStore<Roles, Guid>, AngularAppRoleStore>();
container.Register<UserManager<Users, Guid>, AngularAppUserManager>();
container.Register<RoleManager<Roles, Guid>, AngularAppRoleManager>();
//container.RegisterPerWebRequest<SignInManager<Users, Guid>, AngularAppSignInManager>();
container.Register<IdentityFactoryOptions<AngularAppUserManager>, IdentityFactoryOptions<AngularAppUserManager>>();
//container.Register<IAuthenticationManager>(() => HttpContext.Current.GetOwinContext().Authentication);
//container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
// For instance:
// container.Register<IUserRepository, SqlUserRepository>();
}
Now the problem is The controller is not able to register IAuthenticationManager
. I tried using
container.Register<IAuthenticationManager>(
() => HttpContext.Current.GetOwinContext().Authentication);
But that Leaves me with Exception as:
System.InvalidOperationException: No owin.Environment item was found in the context.
In this line
container.Register<IAuthenticationManager>(
() => HttpContext.Current.GetOwinContext().Authentication);
I also tried instead of using HttpContext.Current.GetOwinContext().Authentication
with the configuration mentioned above in public void Configure(app)
method to register using app.Use()
. And then later resolve it via container to get the IAuthenticationManager
. But every possibilities have left me failed.
What am I missing here? Why HttpContext.Current.GetOwinContext().Authentcation
is failing to resolve authentcation from OwinContext?
And if thats not, why is the same configuration via app.Use
also not working?
Solution 1:
As TrailMax already mentioned, the exception you got probably got raised during the call to container.Verify()
. At application start-up time there is no HttpContext
, hence the exception.
Although the removal of the call to container.Verify()
will 'solve' the problem, I would advise against doing this and I will suggest a better solution below.
NightOwl888 references an old article of Mark Seemann (which I highly respect for his work on DI). In that article Mark explains why he thinks that verifying the container is useless. This article however seems outdated, and conflicts with newer articles from Mark. In a newer article Mark explains that one of the big advantages of using Pure DI (that is Dependency Injection without using a DI container) is that it provides the fastest feedback about correctness that you can get. Mark, and the rest of us, obviously values both the compiler's feedback and the feedback from static code analysis tools, as rapid feedback mechanism. Both Simple Injector's .Verify()
and the Diagnostic Services attempt to bring this fast feedback back. In my view, Simple Injector's .Verify()
method takes on the job that the compiler would do for you when doing Pure DI and the Diagnostic Services is in a sense static code analysis tool specialized to your DI configuration.
While it is indeed not possible for a container to do a 100% verification of its configuration, verifying still proved a valuable practice to me. It would be silly to think that a simple call to .Verify()
would result in a completely bug free or even a working application. If somebody may think that this is what ‘verifying’ your DI configuration means, I understand why they would argue that this functionality is worthless. Sounds like a statement of truism. There is no container out there, including Simple Injector, which pretends having such a feature.
You are off course still responsible for writing integration and/or unit tests for e.g. detecting if the order of applied decorators is correct or if all implementations of ISomeService<T>
are indeed registered in the container.
I want to mention 2 specific arguments from Mark’s blog against verifying the container.
It is easy to get into the situation that the container verifies, but still breaks at runtime.
I agree with that, but I do think that the Simple Injector documentation has got some great guidance on how to approach this here.
When doing convention over configuration it's easy to get registrations that shouldn't be in the container anyway.
I never had this problem, because I think it is a sane practice to prevent getting in this situation anyway.
Back to the question:
Although one of the tips in the Simple Injector documentation is to use abstract factories, I wouldn't do that in this case. Creating a factory for something that already exists, sounds pretty weird to me. Maybe it is just a problem of correct naming, but why would an AccountController need a AuthenticationFactory
or AuthenticationContext
? In other words, why should the application know anything about us having problem wiring things up because of some design quirks in ASP.NET Identity?
Instead, by adjusting the registration for the IAuthenticationManager
we can return an Authentication component from a newly created OwinContext at startup/verify time and return the 'normal' or configured AuthenticationManager
at runtime. This will remove the need for a factory and moves the responsibility to the Composition Root where it should be. And lets you inject the IAuthenticationManager
everywhere you need it, while still being able to make a call to .Verify()
.
The code looks like:
container.RegisterPerWebRequest<IAuthenticationManager>(() =>
AdvancedExtensions.IsVerifying(container)
? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication);
An even more SOLID solution however would be to not depend on the IAuthenticationManager
at all, because depending on this interface causes us to violate the Interface Segregation Principle, making it hard to create a proxy implementation for it that delays the creation.
You could do this by defining an abstraction which fits your needs and only your needs. Looking at the identity template calls to the IAuthenticationManager
this abstraction would need nothing more than the .SignIn()
and .SignOut()
methods. This however would force you to completely refactor the crappy AccountController
that you got 'for free' by the Visual Studio template, which can be quite an undertaking.
Solution 2:
What you are doing with IAuthenticationManager
registration worked for me with no issues. At some point I was getting the same exception as you were getting, but that was caused by line with
container.Verify();
just after the container configuration. It was trying to create all instances of registered objects, but there was no HttpContext.Current
present, hence the exception.
Are you not getting any instances out of container before any HTTP request is available? If you really need them, then the only way to work around this is use Factory, as suggested by NightOwl888. If you don't need container before the HTTP request, then refactor, so it is not use outwith HTTP request.