jonpryor / dblinq2007

Automatically exported from code.google.com/p/dblinq2007
0 stars 0 forks source link

DatabaseTransaction.Dispose might fail to clear _currentTransaction #219

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
I have been trying to get the unit tests to run (not succeed, just run) for
all database vendors in order to get the "Tests" page updated in the wiki
and create a new baseline. I noticed that all the test for mysql would
fail, if the test for Ingres had been run (mysql is run just after Ingres).
It turned out that I was using the wrong sql-script for creating the Ingres
database, but I think the problem could arise other ways and should
therefor be handled more gracefully.

Tested using svn rev 1333.

What steps will reproduce the problem?
1. In an Ingres database load the sql-script from
"tests\instnwnd.Ingres.sql" and verified that connection strings and
permissions is configured correct e.g. by verifying that the AnyExternal01
test succeeds.
2. Run the LinqToSqlInsert05 test for the Ingres vendor either using the
NUnit gui runner or in try/catch in an application (since it will fail).
3. Run the AnyExternal01 test for the sqlite vendor (note this is now a
sqlite test, but it could be for any non-Ingres database) in the same NUnit
session or same application. E.g. like this:

// Step 2
try {
  new Test_NUnit_Ingres.Linq_101_Samples.Insert_Update_Delete()
    .LinqToSqlInsert05();
} catch (Exception ex) { }
// Step 3
new Test_NUnit_Sqlite.AnyCount().AnyExternal01();

What is the expected output? What do you see instead?
The AnyExternal01 test should pass just as it would have, had step 2 not
been performed.

Instead it fails with the exception:
"Unable to cast object of type 'Ingres.Client.IngresTransaction' to type
'System.Data.SQLite.SQLiteTransaction'."
at System.Data.SQLite.SQLiteCommand.set_DbTransaction(DbTransaction value)
at
System.Data.Common.DbCommand.System.Data.IDbCommand.set_Transaction(IDbTransacti
on
value)
at DbLinq.Data.Linq.Database.Implementation.DatabaseContext.CreateCommand()
in ...\DbLinq\Data\Linq\Database\Implementation\DatabaseContext.cs:line 117
at
DbLinq.Data.Linq.Database.Implementation.TransactionalCommand..ctor(String
commandText, Boolean createTransaction, DataContext dataContext) in
...\DbLinq\Data\Linq\Database\Implementation\TransactionalCommand.cs:line 98
at DbLinq.Data.Linq.Sugar.AbstractQuery.GetCommand(Boolean
createTransaction) in ...\DbLinq\Data\Linq\Sugar\AbstractQuery.cs:line 51
at DbLinq.Data.Linq.Sugar.SelectQuery.GetCommand() in
...\DbLinq\Data\Linq\Sugar\SelectQuery.cs:line 88
at DbLinq.Data.Linq.Sugar.Implementation.QueryRunner.Select[T](SelectQuery
selectQuery) in
...\DbLinq\Data\Linq\Sugar\Implementation\QueryRunner.cs:line 65
at
DbLinq.Data.Linq.Sugar.Implementation.QueryRunner.SelectSingle[S](SelectQuery
selectQuery, Boolean allowDefault) in
...\DbLinq\Data\Linq\Sugar\Implementation\QueryRunner.cs:line 170
at
DbLinq.Data.Linq.Sugar.Implementation.QueryRunner.SelectScalar[S](SelectQuery
selectQuery) in
...\DbLinq\Data\Linq\Sugar\Implementation\QueryRunner.cs:line 125
at
DbLinq.Data.Linq.Implementation.QueryProvider`1.Execute[TResult](Expression
expression) in ...\DbLinq\Data\Linq\Implementation\QueryProvider.cs:line 193
at System.Linq.Queryable.Any[TSource](IQueryable`1 source)
at Test_NUnit_Sqlite.AnyCount.AnyExternal01() in
...\DbLinq\Test\Providers\ReadTests_AnyCountFirst.cs:line 101
at TransactionTest.Program.Main(String[] args) in
...\TransactionTest\Program.cs:line 24

Please use labels and text to provide additional information.
It turns out that the reason that the Ingres Insert_Update_Delete test
fails is that the execution of the sql query 'UPDATE "linquser"."products"
SET "unitprice" = ? WHERE "productid" = ?' fails with the error "Server
aborted connection", with the source "Ingres .NET Data Provider" and the
stack trace:
at Ingres.ProviderInternals.AdvanPrep.exec(ParamSet param_set)
at Ingres.ProviderInternals.AdvanPrep.execute()
at Ingres.Client.IngresCommand.Execute()
at Ingres.Client.IngresCommand.ExecuteNonQuery()
at DbLinq.Data.Linq.Sugar.Implementation.QueryRunner.Upsert(Object target,
UpsertQuery insertQuery) in
...\DbLinq\Data\Linq\Sugar\Implementation\QueryRunner.cs:line 224
at DbLinq.Data.Linq.Sugar.Implementation.QueryRunner.Update(Object target,
UpsertQuery updateQuery, IList`1 modifiedMembers) in
...\DbLinq\Data\Linq\Sugar\Implementation\QueryRunner.cs:line 274
at DbLinq.Data.Linq.DataContext.UpdateEntity(Object entity, QueryContext
queryContext) in ...\DbLinq\Data\Linq\DataContext.cs:line 523
at DbLinq.Data.Linq.DataContext.SubmitChangesImpl(ConflictMode failureMode)
in ...\DbLinq\Data\Linq\DataContext.cs:line 458
at DbLinq.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode) in
...\DbLinq\Data\Linq\DataContext.cs:line 407
at DbLinq.Data.Linq.DataContext.SubmitChanges() in
...\DbLinq\Data\Linq\DataContext.cs:line 369
at
Test_NUnit_Ingres.Linq_101_Samples.Insert_Update_Delete.LinqToSqlInsert05()
in ...\DbLinq\Test\Providers\Linq_101_Samples\Insert_Update_Delete.cs:line 233
at TransactionTest.Program.Main(String[] args) in
...\TransactionTest\Program.cs:line 11

I don't think that it is important why the database closes the connection.
That is not what this issue is about. Other vendors might close the
connection for different reasons or network or disk issue might cause it.
The problem is that the transaction is now in a state that might cause the
Rollback to throw an exception.

It means that when the exception propagates through the using statement in
DataContext.SubmitChanges(ConflictMode) (DataContext.cs line 402) the
DatabaseTransaction.Dispose() (DatabaseTransaction.cs line 89) method is
called. But the Dispose method fails since the _transaction.Roolback()
fails (message: "Connection closed.", source: "Ingres .NET Data Provider").
This means (looking at the Dispose method), that the _transaction isn't
Disposed, and that _currentTransaction isn't set to null. The later means
that the transaction is reused when the next test is run (although it is
using a different database vendor).

In the attached patch I've added some try/finally statements (to many?) as
well as a "_transaction = null;" (since Dispose method should handle
multiple executions). This allows the first test to fail, while allowing
the second test to succeed.

I think it looks a little ugly to have "_currentTransaction" as a static
field, but I guess the object is supposed to only exist within a single
DbLinq method so the field should always be null while user code is running.

Original issue reported on code.google.com by anders...@gmail.com on 22 Mar 2010 at 12:41

Attachments:

GoogleCodeExporter commented 9 years ago
So, the primary reason this happens is because the Ingres DB connection is 
being 
closed.  (If it weren't being closed, things would work ~sanely, which is why 
my 
running DbLinq-Sqlite-Sqlserver.nunit actually works...)

That said, this seems like a rather catastrophic failure failure condition, one 
best 
solved by removing DatabaseTransaction._currentTransaction (or at least making 
it not 
static...).

Original comment by jonmpr...@gmail.com on 23 Mar 2010 at 7:08

GoogleCodeExporter commented 9 years ago
This issue was closed by revision r1341.

Original comment by jonmpr...@gmail.com on 23 Mar 2010 at 8:08

GoogleCodeExporter commented 9 years ago
Closing.

Original comment by jonmpr...@gmail.com on 9 Apr 2010 at 7:55