实现原子 Web 服务调用的惯用方式
Idiomatic way to implement atomic web service call
我有一个网络服务可以创建某种实体。
Web 服务中执行实际工作的方法调用 Web 服务的其他几个方法。其中一些方法需要访问数据库来查询或更新数据。
如何确保作为 Web 请求的一部分进行的所有调用都是同一事务的一部分?
例子
我的主要方法打开一个数据库连接(和事务)。我是否应该将数据库连接对象(and/or 事务对象)向下传递给其他方法,以便它们可以重用现有方法而不是打开自己的方法?
我想尽可能接近这里的标准,但我不确定这种情况,因为您发现的大多数示例只执行一个 SQL,通常在 using块。
在我的例子中,我还会使用一个创建数据库连接的 using 块,并在这个块中调用其他几个传递数据库连接的方法。这是好的做法还是有其他方法?
如果你有一个web调用需要在一个事务中做工作,你可以在控制器方法中打开一个TransactionScope
,当所有工作完成后调用TransactionScope.Complete()
,如下:
using (var transaction = new TransactionScope())
{
// Do the work, no need to pass the transaction anywhere
WriteSomethingToTheDatabase();
WriteSomethingElseToTheDatabase();
transaction.Complete();
}
public void WriteSomethingToTheDatabase()
{
// Under the hood, .NET will obtain a connection from the connection pool, and enlist it in the ambient TransactionScope
using (var connection = new SqlConnection(...))
{
// Assuming Dapper for a simpler example. :)
connection.Execute(...);
}
}
public void WriteSomethingElseToTheDatabase()
{
// Under the hood, .NET will obtain a connection from the connection pool, and enlist it in the ambient TransactionScope
using (var connection = new SqlConnection(...))
{
// Assuming Dapper for a simpler example. :)
connection.Execute(...);
}
}
如前所述,您不需要将交易传递到任何地方,因为它作为“环境”交易工作。也就是说,在此处调用的代码中打开的任何新连接无论如何都会自动“登记”在该事务中。你应该总是 open/close 连接尽可能短的时间。也不必担心传递连接,因为连接池在幕后运行以确保为您处理 实际 数据库连接。
一些注意事项包括:
- 事务不能跨网络服务调用工作(这应该是显而易见的)。因此,如果您在客户端中打开一个事务,然后通过 HTTP 调用 2 个或更多执行数据库操作的 Web 服务,则这些 Web 服务进行的任何数据库调用都不会成为客户端事务的一部分。
- 假设所有涉及的数据库引擎都支持,跨不同类型数据库的事务将最终成为“分布式事务”。 MSSQL、MSMQ、RavenDB 都将加入一个“两阶段提交”事务。理想情况下,您希望避免这种情况,并且只让“本地事务”跨连接到单个数据库工作。
- 请注意,根据您的隔离级别,不同事务中的代码 运行(例如,从不同用户进入服务器的另一个 Web 请求,在不同线程上执行等)将无法查看更改,直到您提交(通过
TransactionScope.Complete()
)。
我有一个网络服务可以创建某种实体。 Web 服务中执行实际工作的方法调用 Web 服务的其他几个方法。其中一些方法需要访问数据库来查询或更新数据。
如何确保作为 Web 请求的一部分进行的所有调用都是同一事务的一部分?
例子
我的主要方法打开一个数据库连接(和事务)。我是否应该将数据库连接对象(and/or 事务对象)向下传递给其他方法,以便它们可以重用现有方法而不是打开自己的方法?
我想尽可能接近这里的标准,但我不确定这种情况,因为您发现的大多数示例只执行一个 SQL,通常在 using块。
在我的例子中,我还会使用一个创建数据库连接的 using 块,并在这个块中调用其他几个传递数据库连接的方法。这是好的做法还是有其他方法?
如果你有一个web调用需要在一个事务中做工作,你可以在控制器方法中打开一个TransactionScope
,当所有工作完成后调用TransactionScope.Complete()
,如下:
using (var transaction = new TransactionScope())
{
// Do the work, no need to pass the transaction anywhere
WriteSomethingToTheDatabase();
WriteSomethingElseToTheDatabase();
transaction.Complete();
}
public void WriteSomethingToTheDatabase()
{
// Under the hood, .NET will obtain a connection from the connection pool, and enlist it in the ambient TransactionScope
using (var connection = new SqlConnection(...))
{
// Assuming Dapper for a simpler example. :)
connection.Execute(...);
}
}
public void WriteSomethingElseToTheDatabase()
{
// Under the hood, .NET will obtain a connection from the connection pool, and enlist it in the ambient TransactionScope
using (var connection = new SqlConnection(...))
{
// Assuming Dapper for a simpler example. :)
connection.Execute(...);
}
}
如前所述,您不需要将交易传递到任何地方,因为它作为“环境”交易工作。也就是说,在此处调用的代码中打开的任何新连接无论如何都会自动“登记”在该事务中。你应该总是 open/close 连接尽可能短的时间。也不必担心传递连接,因为连接池在幕后运行以确保为您处理 实际 数据库连接。
一些注意事项包括:
- 事务不能跨网络服务调用工作(这应该是显而易见的)。因此,如果您在客户端中打开一个事务,然后通过 HTTP 调用 2 个或更多执行数据库操作的 Web 服务,则这些 Web 服务进行的任何数据库调用都不会成为客户端事务的一部分。
- 假设所有涉及的数据库引擎都支持,跨不同类型数据库的事务将最终成为“分布式事务”。 MSSQL、MSMQ、RavenDB 都将加入一个“两阶段提交”事务。理想情况下,您希望避免这种情况,并且只让“本地事务”跨连接到单个数据库工作。
- 请注意,根据您的隔离级别,不同事务中的代码 运行(例如,从不同用户进入服务器的另一个 Web 请求,在不同线程上执行等)将无法查看更改,直到您提交(通过
TransactionScope.Complete()
)。