TransactionScope does not rollback inside wcf service method, does roll back if called directly
Asked Answered
P

2

7

I am facing a problem that drives me crazy for couple of days now, hoping someone can help me. Here it is ;

I'm using EF4 with oracle database, using dotConnect for oracle from devart as provider. I have wcf service method which calls DeleteCabinet method below;

public void DeleteCabinet(string pRID)
{
    using(TransactionScope tranScope = new TransactionScope())
    {
        DBUtils.DeleteCabinetAndShelves(pRecordId);

        //throw exception to test record not deleted
        throw new Exception("xxx something has happened test xxx");

        tranScope.Complete();
    }
}

DBUtils.DeleteCabinetAndShelves looks like below;

public void DeleteCabinetAndShelves(string pRecordId)
{
    using(var context = new EdrmEntities())
    {
        var cabinet = context.Cabinets.Include("Shelves").Single(p => p.RID == pCabinetRID);

        //mark all cabinet shelves for deletion
        if (cabinet.Shelves != null)
        {
            foreach (var tempShelf in cabinet.Shelves.ToList())
            {
                context.DeleteObject(tempShelf);
            }
        }

        //mark cabinet for deletion
        context.DeleteObject(cabinet);

        //save 
        context.SaveChanges();
    }
}

when I call DeleteCabinet from within my test project, not a wcf call but direct method call, it works OK. It throws exception ,and transaction is rolled back. Thus no record is deleted from DB as expected

The problem is that when I call service method (which calls DeleteCabinet) from a client, the exception is thrown , but the record IS deleted from db. Transaction does not roll back !

seems like calling wcf method does not roll back transaction, but it seems crazy ( at least to me), does anybody know the reason why this might be happening ?

Thanks in advance

Palatinate answered 5/3, 2012 at 12:32 Comment(8)
I believe this would largely depend on your WCF bindings and whether they have transactional support. Update your question to reveal your bindings and your WCF service contract declarations.Binnacle
Correct me if I'm wrong, but since my service is not participating in a transaction started by a client (no wcf transaction in that sense) ,those settings should be irrelevant in this scenario. i.e : client call to service method is not inside transactionscope, it's the server that starts and completes transaction as seen above.Palatinate
Ohh, I see, thats odd. Do you allow WCF to fault the channel or do you handle the exception in the WCF service call and send a result back to the client? Is EdrmEntities constructing it's own EntityConnection or is it perhaps using a shared connection that has been automatically enlisted in an implicit transaction outside your TransactionScope?Binnacle
Hi . ..class EdrmEntities: ObjectContext { ... and ctor calls base as following EdrmEntities():base("name=EdrmEntities", "EdrmEntities"). Btw how can I understand if shared connection,if used, is enlisted in an implicit transaction outside of my scope ?Palatinate
About faulting : I tried it both ways catching exception on server and letting it fault the channel, both ways result is the same.Palatinate
If you derive from ObjectContext, using a named connection string (name=EdrmEntities), then I believe a new connection is opened (not a shared one). I guess the best way to determine if there is an ambient transaction outside your TransactionScope is to check the Transaction.Current property before your new scope is created; automatic transaction enlistment is specific to the configuration of the connection string for your provider (Enlist=false). Hmmm...Binnacle
I noticed I did not provide my connection string anywhere, my bad. Here it is : connectionString="metadata=res://*/Model.EdrmModel.csdl|res://*/Model.EdrmModel.ssdl|res://*/Model.EdrmModel.msl;provider=Devart.Data.Oracle;provider connection string="User Id=XXX;Password=YYY;Server=ZZZ;Max Pool Size=3000;Pooling=True;Enlist=false;Transaction Scope Local=true""Palatinate
As a previous commenter alluded to, your use of Enlist=false in that connection string may be the culprit. I believe that option means that the any connection you create will not enlist itself in the transaction managed by the thread's current TransactionScope (or Transaction.Current). Try changing it to Enlist=true.Abaxial
A
3

You tagged your post with the DevArt and DotConnect tags... I wonder if this is a bug in the DevArt providers rather than something inherent to WCF / Entity Framework / System.Transactions. You could test the theory by seeing if it happens with a ObjectContext that is using the built-in SQL Server Provider (or even Oracle's own EF provider that was recently released) and see if the issue still occurs. That is the only thing I can think of since the code seems 100% correct.

Abaxial answered 6/3, 2012 at 13:25 Comment(1)
Tried with Oracle's provider , and it seems working. I am figuring if it was a bug in devarts provider, it'd be uncovered long before (and fixed), so I am thinking it must have something to do with the configuration . Will contact their support team for the issue. `Palatinate
P
1

Thanks to @Rabid and @luksans constructive comments problem is solved, and it turned out it has nothing to do with wcf or devart's provider being buggy

Here's the thing ; wcf service (which did not rollback), and integration test (which did) are inside different projects, thus config files are different. They got out of sync sometime in past, and the difference is in Enlist=false part. So wcf project's connection string has Enlist=false while test project does not. That's how the illusion of WCF failing transaction was born.

Removing Enlist=false from wcf project's connection string fixed the issue.

Palatinate answered 7/3, 2012 at 8:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.