问题
I am using ServiceStack.Ormlite v3.9.71 and have the following piece of code where I open an Ormlite SQLite Transaction, suppose I want to use this transaction in a command elsewhere in the code:
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
}
}
however when I do, I get the:
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'.
And the Stack Trace:
at System.Data.Common.DbCommand.System.Data.IDbCommand.set_Transaction(IDbTransaction value)
I guess this is due to the OrmLiteTransaction wrapper, how can I get to the transaction?
回答1:
OrmLite provides ToDbTransaction extension method.
public static IDbTransaction ToDbTransaction(this IDbTransaction dbTrans)
{
var hasDbTrans = dbTrans as IHasDbTransaction;
return hasDbTrans != null
? hasDbTrans.Transaction
: dbTrans;
}
You can use it in your code like this.
cmd.Transaction = tran.ToDbTransaction()
Hope it will solve your problem.
回答2:
UPDATE
OrmLiteConnection will return a SqliteCommand which means that it just isn't possible to set the command's Transaction property, because the command expects a SqliteTransaction object. Even if you try to set IDbCommand.Transaction, you'll call the DbCommand.Transaction setter which checks that the object passed is a DbTransaction object
The fix is to simply not set the Transaction property, as db.CreateCommand sets it itself if a transaction was already opened with db.OpenTransaction
Original
I think you should follow @Mangus' advice and use var instead of casting to interfaces.
Checking SqliteOrmLiteDialectProvider's implementation, it appears it's using Mono.Data.Sqlite. Mono.Data.Sqlite classes inherit from the abstract DbXXX classes but hide the base implementations with their own.
SQLiteCommand.Transaction and SQLiteConnection.CreateCommand have the following signatures:
public new SqliteCommand CreateCommand()
{
...
}
public new SqliteTransaction Transaction
{
...
}
By casting to the interfaces you call the base implementation in the DbConnection, DbCommand classes instead of the new implementations in Mono.Data.Sqlite. This means that OpenTransaction will return a DbTransaction-derived object instead of an OrmLiteTransaction object.
The following code should work:
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;
}
}
ADO.NET providers since .NET 2.0 are expected to derive from the DbXXX abstract classes instead of implementing the .NET 1.1 interfaces like IDbConnection.
In your case, OrmLite breaks the expected behaviour resulting in a clash with Mono.Data.Sqlite's unexpected member hiding. Otherwise you wouldn't have noticed anything.
来源:https://stackoverflow.com/questions/26234765/why-can-i-not-cast-idbtransaction-in-servicestack-ormlite-to-dbtransaction