Autofac:如何在 SingleInstance 范围内使用每个请求依赖项?

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

IDbConnection 个注册为 per-request 的连接依赖项和 ApplicationOAuthProvider 个注册为 single instance 的连接依赖项。

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

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

需要在身份声明中存储用户权限。为此,我创建了命令 GetRolePermissions,在此命令中我想注入 IDbConnection 实例。

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;
        }
    }
}

此命令正在 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 }); - 抛出错误,因为 GetRolePermissions.Handler 需要注入 IDbConnection,但当前 ApplicationOAuthProvider 的 autofac 范围是 "root" 并且没有 iDbConnection 在此范围内注册。但它存在于 per-request 范围内。

不想对 IDbConnection 使用 perLifettime,因为我认为 dbConnection 应该在不需要时关闭。我想这样做:

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

但无法使该解决方案起作用。而且我不知道如何正确获取容器,目前它是我手动设置的 AutofacConfig 静态变量。

如何将 IDbConnection 注入到这个 GetPermissions.Handler 中?

实际上,InstancePerLifetimeScope() 就是您要寻找的解决方案。您需要做的就是注入 DbConnection factory 来生成拥有的 (docs link 1 and docs link 2) DbConnection 实例,而不是 DbConnection 本身。

// 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
        }
    }
}

它确实改变了作为请求范围子级的范围的行为,但我不确定这是否是您的代码的问题 - 试一试,它应该可以正常工作。如果没有,那么您知道去哪里问。 ;)