Search code examples
vb.netsimple-injector

Simple Injector instance is requested outside the context of an active (async scope) scope


I'm trying to inject my IAuthorizationRepository into my VB Web App using SimpleInjector.

Public Class IdentityPackage
    Implements IPackage
    Public Sub RegisterServices(container As Container) Implements IPackage.RegisterServices
        container.Register(Of IUserStore(Of User, Integer), UserStore)(Lifestyle.Scoped)
        container.Register(Of IAuthorizationRepository, AuthorizationRepository)(Lifestyle.Scoped)
        container.Register(Of ISession, Session)()
    End Sub
End Class

Here's my startup.

Public Partial Class Startup
    Public Sub Configuration(app As IAppBuilder)
        Dim container = ConfigureSimpleInjector(app)
        Dim config = New HttpConfiguration() With {
            .DependencyResolver = New SimpleInjectorWebApiDependencyResolver(container)
        }

        ConfigureOAuth(app, container)

        WebApiConfig.Register(config)
        app.UseCors(CorsOptions.AllowAll)
        app.UseWebApi(config)
    End Sub
End Class

And here's my ConfigureOAuth.

Public Sub ConfigureOAuth(app As IAppBuilder, container As Container)
    Dim authRepositoryFactory As Func(Of IAuthorizationRepository) = container.GetInstance(Of IAuthorizationRepository)

    Dim authorizationOptions = New OAuthAuthorizationServerOptions() With {
        .AllowInsecureHttp = True,
        .TokenEndpointPath = New PathString("/api/token"),
        .AccessTokenExpireTimeSpan = TimeSpan.FromHours(4),
        .Provider = New AuthorizationServerProvider(authRepositoryFactory)
    }

    ' Token Generation
    app.UseOAuthAuthorizationServer(authorizationOptions)
    app.UseOAuthBearerAuthentication(New OAuthBearerAuthenticationOptions())
End Sub

When it gets to the first line under ConfigureOAuth, it throws the error The AuthorizationRepository is registered as 'Async Scoped' lifestyle, but the instance is requested outside the context of an active (Async Scoped) scope.

The strange thing is that I'm converting an existing, and fully functional C# project into VB, and this is the VB equivalent of the codebase in C#, but the C# code does not have this problem. This makes me feel like the issue lies in how VB handles the code, as opposed to C#, but I don't see how that could be the case with this example. Can anyone tell me what's going wrong?

The following is my stack trace:

[ActivationException: The AuthorizationRepository is registered as 'Async Scoped' lifestyle, but the instance is requested outside the context of an active (Async Scoped) scope.]
   SimpleInjector.Scope.GetScopelessInstance(ScopedRegistration`1 registration) +168
   SimpleInjector.Scope.GetInstance(ScopedRegistration`1 registration, Scope scope) +52
   SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope) +158
   lambda_method(Closure ) +223
   SimpleInjector.InstanceProducer.BuildAndReplaceInstanceCreatorAndCreateFirstInstance() +32
   SimpleInjector.InstanceProducer.GetInstance() +235
   SimpleInjector.Container.GetInstanceForRootType() +154
   SimpleInjector.Container.GetInstance() +146
   ClientWebAppVB.Api.Startup.ConfigureOAuth(IAppBuilder app, Container container) in C:\Projects\TEST\ClientWebAppVB\ClientWebAppVB.Api\App_Start\OAuthConfig.vb:10
   ClientWebAppVB.Api.Startup.Configuration(IAppBuilder app) in C:\Projects\TEST\ClientWebAppVB\ClientWebAppVB.Api\Startup.vb:15

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
   System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
   System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +160
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +101
   Owin.Loader.<>c__DisplayClass12.<MakeDelegate>b__b(IAppBuilder builder) +66
   Owin.Loader.<>c__DisplayClass1.<LoadImplementation>b__0(IAppBuilder builder) +123
   Microsoft.Owin.Host.SystemWeb.<>c__DisplayClass2.<InitializeBlueprint>b__0(IAppBuilder builder) +71
   Microsoft.Owin.Host.SystemWeb.OwinAppContext.Initialize(Action`1 startup) +462
   Microsoft.Owin.Host.SystemWeb.OwinBuilder.Build(Action`1 startup) +40
   Microsoft.Owin.Host.SystemWeb.OwinHttpModule.InitializeBlueprint() +70
   System.Threading.LazyInitializer.EnsureInitializedCore(T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory) +115
   Microsoft.Owin.Host.SystemWeb.OwinHttpModule.Init(HttpApplication context) +106
   System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +536
   System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +173
   System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +336
   System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +296

[HttpException (0x80004005): Exception has been thrown by the target of an invocation.]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +10044576
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +95
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +254

Solution

  • The strange thing is that I'm converting an existing, and fully functional C# project into VB, and this is the VB equivalent of the codebase in C#, but the C# code does not have this problem

    Well this is actually not the equivalent of the c# version. And unless you changed Option Explicit and Option Strict settings I'm pretty sure the code you show does not even compile. Or in your real code you omitted the As Func(Of IAuthorizationRepository) clause.

    In c# this two lines are 2 totally different things:

    // create a delegate
    Func<IAuthorizationRepository> authRepositoryFactory = 
                            container.GetInstance<IAuthorizationRepository>;
    // and directly call a delegate or method
    Func<IAuthorizationRepository> authRepositoryFactory = 
                            container.GetInstance<IAuthorizationRepository>();
    

    And here the second line does not compile. This line returns IAuthorizationRepository and therefore is not assignable to Func<IAuthorizationRepository>.

    In Vb.Net however the following lines are exactly the same. The braces of a method call are totally optional.

    ' Directly call the method
    Dim authRepositoryFactory = container.GetInstance(Of IAuthorizationRepository)
    ' And directy call the method
    Dim authRepositoryFactory = container.GetInstance(Of IAuthorizationRepository)()
    

    If you hover over authRepositoryFactory you'll see that the inferred type is IAuthorizationRepository for both lines.

    To create a delegate instead of directly invoking it in VB you need to use the AddressOf Operator

    Dim authRepositoryFactory = New Func(Of IAuthorizationRepository)(
                  AddressOf container.GetInstance(Of IAuthorizationRepository))
    
    'or
    Dim authRepositoryFactory As Func(Of IAuthorizationRepository) =
                  AddressOf container.GetInstance(Of IAuthorizationRepository)
    

    If you would hover over authRepositoryFactory you'll see this is actually a Func (Of IAuthorizationRepository)