Mik*_*ike 17 c# sql transactions
在我的C#代码中,我使用TransactionScope,因为我被告知不要依赖我的sql程序员将始终使用事务,我们负责和yada yada.
话说回来
它看起来像TransactionScope对象在SqlTransaction之前回滚?这是可能的,如果是这样,在事务中包装TransactionScope的正确方法是什么.
这是sql测试
CREATE PROC ThrowError
AS
BEGIN TRANSACTION --SqlTransaction
SELECT 1/0
IF @@ERROR<> 0
BEGIN
ROLLBACK TRANSACTION --SqlTransaction
RETURN -1
END
ELSE
BEGIN
COMMIT TRANSACTION --SqlTransaction
RETURN 0
END
go
DECLARE @RESULT INT
EXEC @RESULT = ThrowError
SELECT @RESULT
Run Code Online (Sandbox Code Playgroud)
如果我运行这个,我得到除以0并返回-1
从C#代码调用我得到一个额外的错误消息
遇到零除错误.
EXECUTE之后的事务计数表示缺少COMMIT或ROLLBACK TRANSACTION tatement.先前的计数= 1,当前计数= 0.
如果我给sql事务一个名字然后
无法回滚SqlTransaction.未找到该名称的任何事务或保存点.EXECUTE之后的事务计数表示缺少COMMIT或ROLLBACK TRANSACTION语句.先前的计数= 1,当前计数= 2.
有时似乎计数上升,直到应用程序完全退出
c#就是这样
using (TransactionScope scope = new TransactionScope())
{
... Execute Sql
scope.Commit()
}
Run Code Online (Sandbox Code Playgroud)
编辑:
sql代码必须适用于2000和2005
KM.*_*KM. 22
有一个大规模升级到错误的SQL Server 2005中处理这些文章是相当广泛: 错误处理SQL 2005中,后来被厄兰Sommarskog和错误处理SQL 2000 -由厄兰Sommarskog后台
最好的方法是这样的:
创建您的存储过程,如:
CREATE PROCEDURE YourProcedure
AS
BEGIN TRY
BEGIN TRANSACTION --SqlTransaction
DECLARE @ReturnValue int
SET @ReturnValue=NULL
IF (DAY(GETDATE())=1 --logical error
BEGIN
SET @ReturnValue=5
RAISERROR('Error, first day of the month!',16,1) --send control to the BEGIN CATCH block
END
SELECT 1/0 --actual hard error
COMMIT TRANSACTION --SqlTransaction
RETURN 0
END TRY
BEGIN CATCH
IF XACT_STATE()!=0
BEGIN
ROLLBACK TRANSACTION --only rollback if a transaction is in progress
END
--will echo back the complete original error message to the caller
--comment out if not needed
DECLARE @ErrorMessage nvarchar(400), @ErrorNumber int, @ErrorSeverity int, @ErrorState int, @ErrorLine int
SELECT @ErrorMessage = N'Error %d, Line %d, Message: '+ERROR_MESSAGE(),@ErrorNumber = ERROR_NUMBER(),@ErrorSeverity = ERROR_SEVERITY(),@ErrorState = ERROR_STATE(),@ErrorLine = ERROR_LINE()
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber,@ErrorLine)
RETURN ISNULL(@ReturnValue,1)
END CATCH
GO
Run Code Online (Sandbox Code Playgroud)
但是,这仅适用于SQL Server 2005及更高版本.如果不在SQL Server 2005中使用TRY-CATCH块,则很难删除SQL Server发回的所有消息.在extra messages你指的是通过回滚如何使用@@ TRANCOUNT处理的性质造成的:
来自http://www.sommarskog.se/error-handling-I.html#trancount
@@ trancount是一个全局变量,它反映了嵌套事务的级别.每个BEGIN TRANSACTION将@@ trancount增加1,每个COMMIT TRANSACTION减少@@ trancount 1.在@@ trancount达到0之前,实际上没有任何内容.ROLLBACK TRANSACTION将所有内容回滚到最外面的BEGIN TRANSACTION(除非您使用了相当异乎寻常的内容) SAVE TRANSACTION),并强制@@ trancount为0,关于先前的值.
当您退出存储过程时,如果@@ trancount与程序开始执行时的值不同,则SQL Server会引发错误266.但是,如果从触发器直接调用过程,则不会引发此错误.或间接的.如果您使用SET IMPLICIT TRANSACTIONS ON运行,也不会引发此问题
如果您不希望收到有关事务计数不匹配的警告,则您只需要在任何时候打开一个事务.您可以通过创建所有这样的过程来完成此操作:
CREATE PROC YourProcedure
AS
DECLARE @SelfTransaction char(1)
SET @SelfTransaction='N'
IF @@trancount=0
BEGIN
SET @SelfTransaction='Y'
BEGIN TRANSACTION --SqlTransaction
END
SELECT 1/0
IF @@ERROR<> 0
BEGIN
IF @SelfTransaction='Y'
BEGIN
ROLLBACK TRANSACTION --SqlTransaction
END
RETURN -1
END
ELSE
BEGIN
IF @SelfTransaction='Y'
BEGIN
COMMIT TRANSACTION --SqlTransaction
END
RETURN 0
END
GO
Run Code Online (Sandbox Code Playgroud)
通过执行此操作,您只会在事务中尚未发出事务命令.如果以这种方式编写所有过程,只有发出BEGIN TRANSACTION的过程或C#代码才会实际发出COMMIT/ROLLBACK,并且事务计数将始终匹配(您不会收到错误).
来自TransactionScope类文档的 C#:
static public int CreateTransactionScope(
string connectString1, string connectString2,
string commandText1, string commandText2)
{
// Initialize the return value to zero and create a StringWriter to display results.
int returnValue = 0;
System.IO.StringWriter writer = new System.IO.StringWriter();
try
{
// Create the TransactionScope to execute the commands, guaranteeing
// that both commands can commit or roll back as a single unit of work.
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// Create the SqlCommand object and execute the first command.
SqlCommand command1 = new SqlCommand(commandText1, connection1);
returnValue = command1.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command1: {0}", returnValue);
// If you get here, this means that command1 succeeded. By nesting
// the using block for connection2 inside that of connection1, you
// conserve server and network resources as connection2 is opened
// only when there is a chance that the transaction can commit.
using (SqlConnection connection2 = new SqlConnection(connectString2))
{
// The transaction is escalated to a full distributed
// transaction when connection2 is opened.
connection2.Open();
// Execute the second command in the second database.
returnValue = 0;
SqlCommand command2 = new SqlCommand(commandText2, connection2);
returnValue = command2.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
}
}
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message);
}
catch (ApplicationException ex)
{
writer.WriteLine("ApplicationException Message: {0}", ex.Message);
}
// Display messages.
Console.WriteLine(writer.ToString());
return returnValue;
}
Run Code Online (Sandbox Code Playgroud)
只是一个想法,但您可能能够使用TransactionAbortedExceptioncatch来获取实际错误并忽略事务计数不匹配警告.
Han*_*ant 13
不要在使用交易双方你的C#代码,并在存储过程.一个就够了.几乎总是应该是你的C#代码,只有它知道dbase的哪些更新应该被拒绝或全部提交.