为什么我不能将ServiceStack OrmLite中的IDbTransaction转换为DbTransaction?

时间:2014-10-07 11:18:08

标签: c# sqlite ormlite-servicestack

我正在使用ServiceStack.Ormlite v3.9.71并在我打开Ormlite SQLite事务时使用以下代码,假设我想在代码中的其他命令中使用此事务:

var connFactory = new OrmLiteConnectionFactory("ConnectionString", SqliteOrmLiteDialectProvider.Instance)
using (IDbConnection db = connFactory.Open()) // using var doesn't make a difference
using (IDbTransaction tran = db.OpenTransaction()) // using var or even db.BeginTransaction() doesn't make a difference
{
    // I do lots of boring stuff
    ...

    // Somewhere else in the code
    using (var cmd = db.CreateCommand())
    {
        cmd.Transaction = tran; // get an error
    }
}    

然而,当我这样做时,我得到了:

A first chance exception of type 'System.InvalidCastException' occurred in System.Data.dll

Additional information: Unable to cast object of type 'ServiceStack.OrmLite.OrmLiteTransaction' to type 'System.Data.Common.DbTransaction'. 

Stack Stack:

   at System.Data.Common.DbCommand.System.Data.IDbCommand.set_Transaction(IDbTransaction value)

我想这是由于OrmLiteTransaction包装器,我怎样才能进入交易?

2 个答案:

答案 0 :(得分:4)

OrmLite提供ToDbTransaction扩展方法。

public static IDbTransaction ToDbTransaction(this IDbTransaction dbTrans)
    {
        var hasDbTrans = dbTrans as IHasDbTransaction;
        return hasDbTrans != null
            ? hasDbTrans.Transaction
            : dbTrans;
    }

您可以在代码中使用它。

cmd.Transaction = tran.ToDbTransaction()

希望它能解决你的问题。

答案 1 :(得分:1)

<强>更新

OrmLiteConnection将返回一个SqliteCommand,这意味着它无法设置命令的Transaction属性,因为该命令需要一个SqliteTransaction对象。即使您尝试设置IDbCommand.Transaction,也要调用DbCommand.Transaction setter来检查传递的对象是否为DbTransaction对象

修复是不设置Transaction属性,因为如果已使用db.CreateCommand

打开了一个事务,db.OpenTransaction会自行设置它

<强>原始

我认为你应该关注@ Mangus&#39;建议并使用var而不是转换为接口。

检查SqliteOrmLiteDialectProvider的实施情况,它似乎正在使用Mono.Data.Sqlite。 Mono.Data.Sqlite类继承自抽象DbXXX类,但隐藏基础实现与自己的。

SQLiteCommand.TransactionSQLiteConnection.CreateCommand具有以下签名:

public new SqliteCommand CreateCommand()
{
...
}

public new SqliteTransaction Transaction
{
...
}

通过强制转换为接口,可以调用DbConnection,DbCommand类中的 base 实现,而不是Mono.Data.Sqlite中的新实现。这意味着OpenTransaction将返回DbTransaction派生的对象而不是OrmLiteTransaction对象。

以下代码应该有效:

var connFactory = new OrmLiteConnectionFactory("ConnectionString", SqliteOrmLiteDialectProvider.Instance)
using (var db = connFactory.Open())
using (var tran = db.OpenTransaction()) // or even db.BeginTransaction()
{
    // I do lots of boring stuff
    ...

    // Somewhere else in the code
    using (var cmd = db.CreateCommand())
    {
        cmd.Transaction = tran; 
    }
}    

自.NET 2.0以来,ADO.NET提供程序应该派生自DbXXX抽象类,而不是像IDbConnection那样实现.NET 1.1接口。

在您的情况下,OrmLite打破了预期的行为,导致与Mono.Data.Sqlite的意外成员隐藏冲突。否则你不会注意到任何事情。