4
votes

I'm using AutoFac 3.5 with WebApi Integration 3.3 and Asp.Identity 2.0.1. The problem is that the Asp.Net Identity has a problem when im specyfing MyDbContext as InstancePerRequest. Then I got this kind of errormessage:

No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.

I'm registering the Asp Token provider like this:

public partial class Startup
{
    static Startup()
    {
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            RefreshTokenProvider = (IAuthenticationTokenProvider)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IAuthenticationTokenProvider)),
            Provider = (IOAuthAuthorizationServerProvider)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IOAuthAuthorizationServerProvider)),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
            AllowInsecureHttp = true
        };
    }

    public void ConfigureAuth(IAppBuilder app)
    {
        app.UseOAuthBearerTokens(OAuthOptions);
    }
}

And the AutoFac part look like this:

builder.RegisterType<MyDbContext>().As<DbContext>().InstancePerRequest();
builder.RegisterType<SimpleRefreshToken>().As<IAuthenticationTokenProvider>();
builder.Register(x => new ApplicationOAuthProvider(
                        "self",
                        x.Resolve<Func<UserManager<User>>>()).As<IOAuthAuthorizationServerProvider>();

Has anyone solved this problem? I found this old post ASP.net Identity, IoC and sharing DbContext

EDIT

And also this blog post with a messy workaround http://blogs.msdn.com/b/webdev/archive/2014/02/12/per-request-lifetime-management-for-usermanager-class-in-asp-net-identity.aspx

1

1 Answers

4
votes

I was able to solve the problem by getting the AutofacWebRequest from inside the OwinContext and resolve the UserManager.

The IOwinContext is passed to the Invoke method of each OwinMiddleware. Inside You can find the Enviroment property that is a IDictionary that contains lots of information, including the "autofac:OwinLifetimeScope". You can get this LifetimeScope, that should be designated with the tag "AutofacWebRequest" because it is a nested Scope created for each http request, and resolve the type of objects that you need.

My implementation looks similar to this. Pay attention to the way that I generate the UserManager class inside UserManagerFactory.

public class Startup
{
    static Startup()
    {
        PublicClientId = "self";

        UserManagerFactory = () =>
        {
            //get current Http request Context
            var owinContext = HttpContext.Current.Request.GetOwinContext();

            //get OwinLifetimeScope, in this case will be "AutofacWebRequest"
            var requestScope = owinContext.Environment.ContainsKey("autofac:OwinLifetimeScope");

            if (!owinContext.Environment.Any(a => a.Key == "autofac:OwinLifetimeScope" && a.Value != null))
                throw new Exception("RequestScope cannot be null...");

            Autofac.Core.Lifetime.LifetimeScope scope = owinContext.Environment.FirstOrDefault(f => f.Key == "autofac:OwinLifetimeScope").Value as Autofac.Core.Lifetime.LifetimeScope;

            return scope.GetService(typeof(UserManager<Models.UserModel>)) as UserManager<Models.UserModel>;

        };

        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProviderCustom(PublicClientId, UserManagerFactory),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };
    }

    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static Func<UserManager<Models.UserModel>> UserManagerFactory { get; set; }

    public static string PublicClientId { get; private set; }

    public void Configuration(IAppBuilder app)
    {

        var builder = new ContainerBuilder();

        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        builder.RegisterType<UserModelsConvert>().InstancePerApiRequest();
        builder.RegisterType<UserStoreCustom>().As<IUserStore<Models.UserModel>>().InstancePerApiRequest();
        builder.RegisterType<UserManager<Models.UserModel>>().InstancePerApiRequest();

        //loading other projects
        builder.RegisterModule(new LogicModule());

        var container = builder.Build();

        app.UseAutofacMiddleware(container);

        //// Create the depenedency resolver.
        var resolver = new AutofacWebApiDependencyResolver(container);

        // Configure Web API with the dependency resolver
        GlobalConfiguration.Configuration.DependencyResolver = resolver;

        app.UseOAuthBearerTokens(OAuthOptions);

        //extend lifetime scope to Web API
        app.UseAutofacWebApi(GlobalConfiguration.Configuration);

        //app.UseWebApi(config);

    }
}

I hope that this can help. If missed something let me know.