简体   繁体   中英

Autofac: How to use per request dependency in the SingleInstance scope?

There are IDbConnection connection dependency registered as per-request and ApplicationOAuthProvider registered as single instance .

builder.Register(c => new SqlConnection(WebConfigUtils.DefaultConnectionString))
            .As<IDbConnection>()
            .InstancePerRequest();

builder.RegisterType<ApplicationOAuthProvider>()
            .As<IOAuthAuthorizationServerProvider>()
            .PropertiesAutowired()
            .SingleInstance();

Need to store user permissions in the identity claims. For this purpose I have created command GetRolePermissions, and in this command I want to inject IDbConnection instance.

public class GetRolePermissions
{
    public class Command: IRequest<List<string>>
    {
        public ICollection<AspNetUserRole> UserRoles { get; set; }
    }

    public class Handler : AsyncRequestHandler<Command, List<string>>
    {
        private IDbConnection databaseConnection;

        public Handler(IDbConnection databaseConnection)
        {
            this.databaseConnection = databaseConnection;
        }
    }
}

This command is being created in the ApplicationOAuthProvider

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
    private async Task<ClaimsIdentity> GenerateUserIdentityAsync(AspNetUser user, string authenticationType)
    {
        user.SecurityStamp = Guid.NewGuid().ToString();
        var identity = await mediator.Send(new GenerateUserIdentity.Command
        {
            AuthenticationType = authenticationType,
            User = user
        });

        List<string> permissions = null;
        permissions = await mediator.Send(new GetRolePermissions.Command { UserRoles = user.Roles });
        var permissionClaimValue = permissions != null && permissions.Count > 0
            ? permissions.Aggregate((resultString, permission) => resultString + "," + permission)
            : "";
        identity.AddClaim(new Claim(AuthenticationConstants.Gender, user.Gender.ToString()));
        identity.AddClaim(new Claim(AuthenticationConstants.Permissions, permissionClaimValue));
        return identity;
    }
}

permissions = await mediator.Send(new GetRolePermissions.Command { UserRoles = user.Roles }); - throws an error as GetRolePermissions.Handler need to inject IDbConnection , but current autofac scope of ApplicationOAuthProvider is "root" and there is no iDbConnection registered in this scope. But it exists in the per-request scope.

Don't want to use perLifettime for IDbConnection, because I think, that dbConnection should be closed when it is unneccessary. I was think to do smth like this:

using(var scope = AutofacConfig.container.BeginLifiTime("AutofacWebRequest")) 
{
      permissions = await mediator.Send(new GetRolePermissions.Command { UserRoles = user.Roles });
}

but can't make this solution working. And I don't know how to get the container properly, for now it static variable of AutofacConfig, that I set manually.

How to enable injection of IDbConnection into this GetPermissions.Handler ?

Actually, InstancePerLifetimeScope() is the solution that you look for. All you need to do is to inject DbConnection factory that produces owned ( docs link 1 and docs link 2 ) instances of DbConnection rather then DbConnection itself.

// registration

builder.RegisterType<SqlConnection>()
    .As<IDbConnection>()
    .WithParameter(new NamedParameter("connectionString", WebConfigUtils.DefaultConnectionString))
    .InstancePerLifetimeScope();

// usage

public class GetRolePermissions
{
    public class Command: IRequest<List<string>>
    {
        public ICollection<AspNetUserRole> UserRoles { get; set; }
    }

    public class Handler : AsyncRequestHandler<Command, List<string>>
    {
        private Func<Owned<IDbConnection>> _connectionFactory;

        public Handler(Func<Owned<IDbConnection>> connectionFactory)
        {
            _connectionFactory = connectionFactory;
        }

        // not really sure where your consuming code is, so just something off the top of my head
        public DontKnowYourResultType Handle(GetRolePermissions.Command cmd) {
            using (var ownedConnection = _connectionFactory()) {
                // ownedConnection.Value is the DbConnection you want
                // ... do your stuff with that connection
            } // and connection gets destroyed upon leaving "using" scope
        }
    }
}

It does change behavior for the scopes that are children of request scope, but I'm not sure if it's a problem for your code or not - just give it a try, chances are that it should work fine. If not then you know where to ask. ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM