2

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()

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
bbqchickenrobot
  • 3,592
  • 3
  • 45
  • 67
  • Can you post the full stack trace? – Steven Jan 12 '15 at 19:32
  • 1
    Is it failing at the point of `container.Verify()`? – qujck Jan 12 '15 at 19:45
  • It's failing during the registration of dependencies. This is the line: container.RegisterPerWebRequest(() => container.GetInstance().Authentication); – bbqchickenrobot Jan 12 '15 at 23:28
  • 1
    Have you seen [this](https://simpleinjector.codeplex.com/discussions/539965) and [this](https://simpleinjector.codeplex.com/discussions/564822)? – qujck Jan 13 '15 at 09:06
  • 3
    I see no need in the registration of the IOwinContext. Where do you depend on the OwinContext to be injected in another class. If possible stay away from injecting the OwinContext directly. And if you really need it, inject a factory kind of class. – Ric .Net Jan 13 '15 at 10:07
  • qujck - yes, Verify() blows up which I've seen in other posts as being an issue. I do want to call verify however to make sure everything is wired up properly. NObody wants to submit these as answers? – bbqchickenrobot Jan 14 '15 at 16:55

3 Answers3

15

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.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
Ric .Net
  • 5,540
  • 1
  • 20
  • 39
  • Instead of a fake `FakeAuthenticationManager` you can probably just return a `new OwinContext();` – janhartmann Jan 20 '15 at 10:24
  • Don't think so, the return type is IAuthenticationManager, so return a new OwinContext won' do the job. Furtermore a new OwinContext would not have a AuthenticationManager associated (null), therefore we return an empty one. – Ric .Net Jan 20 '15 at 10:36
  • I mean I would return `new OwinContext().Autentication`, seems to be working fine in my code. But I think I just return a fake one as well, to be sure. – janhartmann Jan 20 '15 at 10:48
  • @janhartmann, it seems like you were right afterall and it simplifies the code. see my edit. – Ric .Net Mar 07 '15 at 14:22
0

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.

Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • 2
    What's the problem of throwing an exception 'every time the application is initialized'? Initialization will only happen once. – Steven Jan 20 '15 at 07:17
  • Incorrect. The application is initialized every time the application pool expires. Also, according to [Best Practices for Exceptions](http://msdn.microsoft.com/en-us/library/seyhszts%28v=vs.110%29.aspx), you should *Design classes so that an exception is never thrown in normal use.* – NightOwl888 Jan 20 '15 at 09:34
  • If you actually need the HttpContext this could be a solution. But if you take a good look at the question the need is not for the HttpContext but rather for the IAuthenticationManager. I used a try catch statement. you could also do a null check for the HttpContext. But this has some problems of it's own. – Ric .Net Jan 20 '15 at 09:35
  • @Ric.Net - Regardless of the exact type that is required, an abstract factory is a good way to create it. The IAuthenticationManager could be returned from a factory that takes an IHttpContextFactory in its constructor. – NightOwl888 Jan 20 '15 at 09:48
  • 1
    A frequently used website/webservice will expire once every day, depending on your configuration. Factories can become a code smell pretty quickly, so if you can avoid them, just don't use them. – Ric .Net Jan 20 '15 at 10:06
0

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);
Kris
  • 545
  • 7
  • 9