【问题标题】:Why can I not cast IDbTransaction in ServiceStack OrmLite to DbTransaction?为什么我不能将 ServiceStack OrmLite 中的 IDbTransaction 转换为 DbTransaction?
【发布时间】:2014-12-29 18:10:48
【问题描述】:

我正在使用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'. 

还有堆栈跟踪:

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

我猜这是由于 OrmLiteTransaction 包装器造成的,我怎样才能进行交易?

【问题讨论】:

  • db 是什么类型,您使用的是哪个连接工厂? (即 OpenTransaction 不是普通 IDbConnection 接口的一部分)
  • 您使用的是哪个版本的 OrmLite? tmpTran 的类型是什么?它是在哪里创建的?
  • @PanagiotisKanavos,已添加/更新,我上次的更新弄乱了代码
  • 尝试使用using(IDbConnection db = connFactory.Open()) 更改为using (var db = connFactory.Open()) 下一行相同。也许不使用该接口的CreateCommand 版本会重新运行可以接受OrmLiteTransaction 的命令
  • @Mangus 对此不确定。 OrmLiteTransaction 实现了 IDbTransaction,但 cmd.Transaction 似乎期望 DbCommand 即使它的类型是 IDbTransaction。在内部,SS 使用Mono.Data.Sqlite。会不会是这个驱动使用了 DbTransaction?

标签: c# sqlite ormlite-servicestack


【解决方案1】:

OrmLite 提供 ToDbTransaction 扩展方法。

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

你可以像这样在你的代码中使用它。

cmd.Transaction = tran.ToDbTransaction()

希望它能解决你的问题。

【讨论】:

    【解决方案2】:

    更新

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

    解决方法是简单地不设置 Transaction 属性,因为 db.CreateCommand 会自行设置它,如果事务已经用 db.OpenTransaction 打开了

    原创

    我认为您应该遵循 @Mangus 的建议并使用 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 的意外成员隐藏发生冲突。否则你不会注意到任何东西。

    【讨论】:

    • 不,它不起作用,我的原始帖子确实有 var 关键字,但为了清楚起见,我删除了它,我得到了完全相同的异常。
    • 根本不设置属性。 CreateCommand 如果事务已打开,则设置 Transaction 属性。您只是不能将 OrmLiteTransaction 传递给 SqliteCommand。原始交易作为私有字段包装在 OrmLiteTransaction 中,因此您也不能使用它
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多