如何在 ABP 框架中使用拦截器包装工作单元
How to wrap the Unit of Work with an interceptor in ABP framework
文章 https://www.codeproject.com/Articles/1080517/Aspect-Oriented-Programming-using-Interceptors-wit 的作者正在解释如何在 "ASPNET Boilerplate" 框架中创建拦截器。在我的例子中,我想创建一个拦截器(或两个拦截器,一个用于开始,另一个用于结束)来包装 Unit of Work
。当任何 AppService
中的方法启动时,我需要调用一个名为 spStart
的存储过程,并在调用提交到数据库时调用 spEnd
。由于 ABP 中的 Unit of Work
也是一个拦截器,这就是我到目前为止所做的:
按照我在上面提供的 link 文章中的步骤,我执行了以下操作:
- 添加了两个拦截器:
StartInterceptor
和 EndInterceptor
他们的 Registrar
classes
- 已在
ApplicationModule
class 中注册
另外在ApplicationModule
我添加了下面的代码:
IocManager.IocContainer.Register(Component.For<IApplicationService>()
.Interceptors(InterceptorReference.ForType<StartInterceptor>()).First,
Component.For<StartInterceptor>()
.Interceptors(InterceptorReference.ForType<EndInterceptor>()).Last,
Component.For<StopInterceptor>());
此代码应使 StartInterceptor
运行 最先,EndInterceptor
运行 最后。考虑到工作单元拦截器将介于两者之间,并且我们将使用逻辑等待被拦截到 return 结果的异步方法,这应该让我们可以选择包装工作单元.然而,这是发生了什么:当 spStart
运行 时,一切正常。这个存储过程 运行s 在工作单元拦截器之前,所以没有问题。但是,当 spEnd
运行s 时,它表示 "Transaction should be disposed before the connection can be used to execute SQL statements" 或 "The operation is not valid for the state of the transaction"... 似乎 UoW 运行s 与我的存储过程并行。有没有其他人对ABP有同样的问题?你是怎么解决的?
如果您遇到事务问题,您可能需要从活动连接中获取活动事务。当您获得交易时,将其分配给您的 sql 命令。
private DbCommand CreateCommand(string commandText, CommandType commandType, params SqlParameter[] parameters)
{
var command = Context.Database.GetDbConnection().CreateCommand();
command.CommandText = commandText;
command.CommandType = commandType;
command.Transaction = GetActiveTransaction();
foreach (var parameter in parameters)
{
command.Parameters.Add(parameter);
}
return command;
}
private void EnsureConnectionOpen()
{
var connection = Context.Database.GetDbConnection();
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
}
private DbTransaction GetActiveTransaction()
{
return (DbTransaction)_transactionProvider.GetActiveTransaction(new ActiveTransactionProviderArgs
{
{"ContextType", typeof(PhoneBookDbContext) },
{"MultiTenancySide", MultiTenancySide }
});
}
public async Task<GetUserByIdOutput> ExecuteMyStoredProcedure(EntityDto input)
{
EnsureConnectionOpen();
using (var command = CreateCommand("SELECT dbo.GetUsernameById(@id)", CommandType.Text, new SqlParameter("@id", input.Id)))
{
var username = (await command.ExecuteScalarAsync()).ToString();
return new GetUserByIdOutput() { Username = username };
}
}
更多信息:https://www.codeproject.com/Articles/1199648/Using-Stored-Procedure-User-Defined-Function-and-V
文章 https://www.codeproject.com/Articles/1080517/Aspect-Oriented-Programming-using-Interceptors-wit 的作者正在解释如何在 "ASPNET Boilerplate" 框架中创建拦截器。在我的例子中,我想创建一个拦截器(或两个拦截器,一个用于开始,另一个用于结束)来包装 Unit of Work
。当任何 AppService
中的方法启动时,我需要调用一个名为 spStart
的存储过程,并在调用提交到数据库时调用 spEnd
。由于 ABP 中的 Unit of Work
也是一个拦截器,这就是我到目前为止所做的:
按照我在上面提供的 link 文章中的步骤,我执行了以下操作:
- 添加了两个拦截器:
StartInterceptor
和EndInterceptor
他们的Registrar
classes - 已在
ApplicationModule
class 中注册
另外在
ApplicationModule
我添加了下面的代码:IocManager.IocContainer.Register(Component.For<IApplicationService>() .Interceptors(InterceptorReference.ForType<StartInterceptor>()).First, Component.For<StartInterceptor>() .Interceptors(InterceptorReference.ForType<EndInterceptor>()).Last, Component.For<StopInterceptor>());
此代码应使 StartInterceptor
运行 最先,EndInterceptor
运行 最后。考虑到工作单元拦截器将介于两者之间,并且我们将使用逻辑等待被拦截到 return 结果的异步方法,这应该让我们可以选择包装工作单元.然而,这是发生了什么:当 spStart
运行 时,一切正常。这个存储过程 运行s 在工作单元拦截器之前,所以没有问题。但是,当 spEnd
运行s 时,它表示 "Transaction should be disposed before the connection can be used to execute SQL statements" 或 "The operation is not valid for the state of the transaction"... 似乎 UoW 运行s 与我的存储过程并行。有没有其他人对ABP有同样的问题?你是怎么解决的?
如果您遇到事务问题,您可能需要从活动连接中获取活动事务。当您获得交易时,将其分配给您的 sql 命令。
private DbCommand CreateCommand(string commandText, CommandType commandType, params SqlParameter[] parameters)
{
var command = Context.Database.GetDbConnection().CreateCommand();
command.CommandText = commandText;
command.CommandType = commandType;
command.Transaction = GetActiveTransaction();
foreach (var parameter in parameters)
{
command.Parameters.Add(parameter);
}
return command;
}
private void EnsureConnectionOpen()
{
var connection = Context.Database.GetDbConnection();
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
}
private DbTransaction GetActiveTransaction()
{
return (DbTransaction)_transactionProvider.GetActiveTransaction(new ActiveTransactionProviderArgs
{
{"ContextType", typeof(PhoneBookDbContext) },
{"MultiTenancySide", MultiTenancySide }
});
}
public async Task<GetUserByIdOutput> ExecuteMyStoredProcedure(EntityDto input)
{
EnsureConnectionOpen();
using (var command = CreateCommand("SELECT dbo.GetUsernameById(@id)", CommandType.Text, new SqlParameter("@id", input.Id)))
{
var username = (await command.ExecuteScalarAsync()).ToString();
return new GetUserByIdOutput() { Username = username };
}
}
更多信息:https://www.codeproject.com/Articles/1199648/Using-Stored-Procedure-User-Defined-Function-and-V