对于事务和msdtc如何协同工作,我有一些基本的困惑。
我有一个基本的服务器/客户端winforms应用程序。该应用程序使用transactionscope来封装在sql服务器上执行的几个sql命令。
当我仅在服务器上启用msdtc网络访问时,该应用程序似乎工作正常。然后有一天,它停止工作,说没有启用网络访问。
现在看来,我必须同时在客户端计算机和服务器上启用msdtc网络访问,才能使transactionscope工作。
客户端或服务器msdtc服务是否执行事务?或者两者兼而有之?
有没有人有关于客户端和服务器都需要msdtc网络访问,还是只需要服务器访问的指导?
发布于 2009-10-14 06:39:22
如果您使用的是MSDTC,那么您需要客户端(应用程序)和服务器(数据库)来运行MSDTC并进行正确的配置。
这可能是痛苦的根源,尤其是在处理防火墙时。如果您遇到问题,请参阅Troubleshooting Problems with MSDTC。它谈到了BizTalk,但它一般适用于MSDTC。DTCPING也是你的朋友。
现在,如果您使用SQL Server 2005和更高版本,仅访问一个数据库,使用一个数据库连接,并且不在应用程序域之间传递事务,则不应要求使用MSDTC。在这些情况下,System.Transactions事务管理器将为您管理事务。如果发生上述任何一种情况,则事务将被提升为分布式事务(事务管理器将是MSDTC)。有关详细信息,请参阅Transaction Management Escalation。
通常,如果您不需要MSDTC,最好避免使用它。也就是说,如果你只处理一个SQL Server数据库,那么试着把你的代码设计成不使用2005+。除了配置麻烦之外,DTC还会带来性能损失,因为对MSDTC的所有调用都在进程外,再加上两阶段提交协议( MSDTC使用的协议)的开销。
至于在你的特定情况下发生了什么,很难说。如果您的代码没有更改,那么防火墙规则可能已经更改了?我还看到Windows更新更改了DTC配置(出于安全考虑),这导致了一个问题。
基于评论的更新:
为了监视事务升级或升级,如果您没有使用任何分布式事务,我认为您可以使用一些分布式事务协调器性能计数器来跟踪提交的事务。如果进行测试,您可以禁用MSDTC,并查看代码是否失败。另一种方法是监视SQL Server中的事务。从编码的角度来看,您可以尝试处理DistributedTransactionStarted事件并执行一些日志记录(但在投入生产之前删除这些代码)。
有关使用单个连接的代码示例,请转到MSDN的TransactionScope页面。基本上,创建一个TransactionScope,创建一个SqlConnection,对SqlConnection做一些工作,关闭连接,调用scope.Complete()。
请注意,如果您使用的是Data Adapter方法,它们会自动管理您的连接,以便关闭连接或将其返回到连接池。无论哪种方式,如果调用了另一个操作,则该事务将被提升为DTC事务。有关更多详细信息,请参阅System.Transactions and connection pooling。
发布于 2009-10-14 14:11:40
为了扩展@Tuzo的解释,这里有一个总是会升级的命令示例:
using(var scope = new TransactionScope())
{
using(var conn = new SqlConnection(connString)){
conn.Open();
//...some command, etc.
}
using(var conn = new SqlConnection(connString)){
conn.Open();
//...some command, etc.
}
scope.Complete();
}在实践中,连接和命令将在另一个类中,等等,但您得到了想法。即使连接字符串指向同一个数据库,它也会使用DTC升级,因为它是两个连接。非升级事务将是:
using(var scope = new TransactionScope())
{
using(var conn = new SqlConnection(connString)){
conn.Open();
//...some command, etc.
//...some other command, etc.
}
scope.Complete();
}无论如何,这是更好的代码,因为您可以打开连接,执行所需的操作,然后尽快关闭。这确实意味着你必须仔细考虑你的连接管理。根据您的应用程序,您可能会以不同的方式实现此功能。例如:
using(var scope = new TransactionScope())
using(var conn = new SqlConnection(connString))
{
conn.Open();
var myService = new MyService(conn);
var myService2 = new MyService2(conn);
myService.DoSomething();
myService2.DoSomething();
scope.Complete();
}有多种方法可以实现这一点。企业库数据访问应用程序块和各种ORM还可以帮助您更有效地处理连接和事务。
发布于 2010-05-15 00:27:12
更新:我找到了一篇文章,解释了为什么当在TransactionScope中的同一数据适配器上仅对GetData和更新进行usign时,事务会从LTM提升到MSDTC,并提供了解决方法。
权威的TableAdapters + Transactions博客帖子http://blah.winsmarts.com/2006/06/18/the-definitive-tableadapters--transactions-blog-post.aspx
我理解一次打开多个连接以升级要分发的事务的部分。但是,我遇到了一个问题,即只有一个连接,并且有一个针对数据库的查询正在升级它。存储过程中也没有任何事务。如果谁有线索,我很想听听。在我的代码示例中,“表(adapter.Update)”将触发一个分布式事务。
我已经从我现有的项目中提取了代码的核心,并简化了大多数正在进行的工作,但我仍然有同样的问题。这基本上是使用表适配器创建一个数据集,并使用一个存储过程设置它以进行选择、插入和删除。我选择与特定用户相关的所有记录。然后,根据其中一条记录是否存在"myPPID“,我会添加或删除它。然后,我调用update方法,通过查看组件服务中的事务统计信息,看到事务升级为分布式。
我的客户端程序使用的是.Net XP Pro SP3和windows Framework3.5。它通过局域网连接到Windows Server2003 R2企业版SP2上的SQL2005数据库。
private void button1_Click(object sender, EventArgs e)
{
int userId = 3;
int myPPId = 881;
using (TransactionScope ts = new TransactionScope())
{
using (DataSet1TableAdapters.AssignedPPTableAdapter adapter
= new MSDTCPromotionTest.DataSet1TableAdapters.AssignedPPTableAdapter())
{
using (DataSet1.AssignedPPDataTable table = adapter.GetData(userId))
{
DataSet1.AssignedPPRow row = table.FindByUserIdmyPPId(
userId, myPPId);
if (row == null)
{
table.AddAssignedPPRow(userId, myPPId, string.Empty,
string.Empty, true);
}
else
{
row.Delete();
}
adapter.Update(table);
}
ts.Complete();
}
}
}连接字符串没有什么特殊之处:
<add name="ConnectionString" connectionString="
Data Source=devdb;
Initial Catalog="TEST MSDTC";
Integrated Security=True"
providerName="System.Data.SqlClient" />此外,存储过程是简单的crud调用。
创建:
ALTER procedure [dbo].[p_UserForm_AssignedPP_Insert]
(
@UserId INT,
@myPPId int
)
AS
SET NOCOUNT ON;
INSERT INTO [UsermyPP] ([UserID],[myPPID],[DateCreated])
VALUES (@UserId,@myPPId,GETutcDATE()) 阅读:
ALTER procedure [dbo].[p_UserForm_AssignedPP_SelectByUserId]
(
@UserId int
)
AS
SELECT
[UserId],
[myPPId],
'' Title,
'' Abbreviation,
0 IsArchived
from
UsermyPP unpp
where
unpp.[userid] = @UserId删除:
ALTER procedure [dbo].[p_UserForm_AssignedPP_Delete]
(
@Original_UserId INT,
@Original_MyPPId INT
)
AS
SET NOCOUNT ON;
DELETE FROM usermypp WHERE [UserID] = @Original_UserId
AND [MyPPID] = @Original_MyPPIdhttps://stackoverflow.com/questions/1564454
复制相似问题