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