前阵子使用FirebirdClient-2.1.0 for-net-2.0 provider 组件访问数据库,因项目中数据访问层可能是WebService,所以在业务层全部使用TransactionScope来控制事务的开启和提交。
在FbConnection组件连接数据库时,可以在连接串中指定Enlist(True支持,False不支持)选项来启用TransactionScope的支持。
using(TransactionScope ts = new TransactionScope())
{
//open connection
//update delete or insert data
//TODO:other transaction suppert operation
ts.Complete();
}
当我在连接串中设置Enlist选项为True后,如果不显示创建TransactionScope,对数据库操作都会引发异常。而一般写一查询数据相关的代码时习惯上都不去创建事务。如下代码会引发异常:
string connectionString = "DataSource=localhost;Database=mydb.fdb;User=SYSDBA;Password=masterkey;Enlist=True";
using(FbConnection conn = new FbConnection(connectionString ))
{
FbCommand cmd = new FbCommand("select * from tb_user");
IDataReader dr = cmd.ExecuteQuery();//此处引发异常.必须先创建一个TransactionScope事务块
//.....
}
必须要创建一个事务块:
using(TransactionScope ts = new TransactionScope())
{
string connectionString = "DataSource=localhost;Database=mydb.fdb;User=SYSDBA;Password=masterkey;Enlist=True";
using(FbConnection conn = new FbConnection(connectionString ))
{
FbCommand cmd = new FbCommand("select * from tb_user");
IDataReader dr = cmd.ExecuteQuery();//此处引发异常.必须先开始一个TransactionScope事务
//.....
}
}
解决方法:
1.修改IL代码
当时忘了有源码直接用ildasm工具将FirebirdSql.Data.FirebirdClient.dll文件转换成il代码,找到
.method public hidebysig instance void
EnlistTransaction(class [System.Transactions]System.Transactions.Transaction transaction) cil managed
在.maxstack 8下面加入
ldnull
ldarg.1
call bool [System.Transactions]System.Transactions.Transaction::op_Equality(
class [System.Transactions]System.Transactions.Transaction
,class [System.Transactions]System.Transactions.Transaction)
brfalse.s IL_0000
ret
再用ilasm工具编译成动态库即可。
2.修改Firebird Data Provider 的源代码,重新编译.
找到FbConnectionInternal类的EnlistTransaction方法if (this.options != null && this.options.Enlist)行,修改为
if (this.options != null && this.options.Enlist && transaction != null),如下所示:
public void EnlistTransaction(System.Transactions.Transaction transaction)
{
//if (this.options != null && this.options.Enlist)
if (this.options != null && this.options.Enlist && transaction != null)//修改后
{
if (this.HasActiveTransaction)
{
throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");
}
if (this.enlistmentNotification != null)
{
throw new ArgumentException("Already enlisted in a transaction");
}
this.enlistmentNotification = new FbEnlistmentNotification(this, transaction);
this.enlistmentNotification.Completed += new EventHandler(EnlistmentCompleted);
}
}
修改完后Build项目,然后将生成的dll文件覆盖D:/Program Files/FirebirdClient 2.0/FirebirdSql.Data.FirebirdClient.dll文件,重新部署到GAC里面即可。
修改后FbConnection组件和SqlConnection类似,如果当前上下文在隐式事务块中则自动加入到事务里,否则和普通数库操作相同。