0

I've got a Publication table and a Receipt table. When I publish data out, I track it in the Publication table. When my test client receives that publication back from the broker, I track it in the Receipt table.

My Publication table has a denormalized ReceiptCount column on it for ease of reporting.

When my test consumer receives the Publication, the following code executes:

try
{
    using (var context = new PublicationVerifierEntities())
    {
        using (var transaction = new TransactionScope())
        {
            if (request.PublicationGuid == Guid.Empty)
            {
                context.AddToUnknownPublications(new UnknownPublication
                {
                    DateReceived = request.DateReceived,
                    Publication = request.Publication,
                });
            }
            else
            {
                // look up the publication by Guid
                var publication = context.Publications.FirstOrDefault(m => m.PublicationGuid.Equals(request.PublicationGuid));

                if (publication == null)
                {
                    throw new Exception("UpdatePublicationReceipt, provided PublicationGuid not found in database.");
                }
                else
                {
                    context.AddToUnknownPublications(new UnknownPublication
                    {
                        DateReceived = request.DateReceived,
                        Publication = request.Publication,
                    });

                    context.AddToReceipts(new Receipt
                    {
                        DateReceived = request.DateReceived,
                        Publication = publication,
                    });

                    publication.ReceiptCount++;
                }
            }

            context.SaveChanges(System.Data.Objects.SaveOptions.None);
            transaction.Complete();
            context.AcceptAllChanges();
        }
    }
}
catch (Exception ex)
{
    _logger.Error("Unable to update publication record receipt.", ex);
    throw;
}

The problem is if the insert on Receipt fails, the Publication.ReceiptCount is never rolled back. To just deleted the Receipt table to test it, and the ReceiptCount column continues to update successfully despite a System.Data.UpdateException being thrown. I see the following queries in SQL Profiler execute, but nothing about rollback:

exec sp_executesql N'update [dbo].[Publication]
set [ReceiptCount] = @0
where ([PublicationId] = @1)
',N'@0 int,@1 int',@0=10,@1=3414639

exec sp_executesql N'insert [dbo].[Receipt]([PublicationId], [DateInserted], [DateReceived])
values (@0, null, @1)
select [ReceiptId]
from [dbo].[Receipt]
where @@ROWCOUNT > 0 and [ReceiptId] = scope_identity()',N'@0 int,@1 datetime',@0=3414639,@1='2013-02-21 18:12:47:513'

This other answer on SO leads me to believe I'm doing everything correctly: How to rollback a transaction in Entity Framework, so...

What am I missing here??

So far I've tried wrapping the TransactionScope using statement around the creation of the context and every overload of SaveChanges(). Nothing made a difference.

TIA!

Community
  • 1
  • 1
Langdon
  • 19,875
  • 18
  • 88
  • 107
  • Why is `SaveChange`, `Complete` outside of TransactionScope block? It means that your `SaveChanges` is not part of the transaction and Complete should most probably fail on object disposed exception. – Ladislav Mrnka Feb 21 '13 at 18:41
  • @LadislavMrnka Sorry, I botched the copy/paste job after I tried editing the code down to the relevant parts. I pasted it in there as is now. – Langdon Feb 21 '13 at 18:59
  • Set a breakpoint on Complete. Is it being executed? – usr Feb 21 '13 at 19:21
  • @usr No, `System.Data.UpdateException` is being thrown on the .SaveChanges() line. – Langdon Feb 21 '13 at 19:23
  • 1
    In SQL Profiler enable transaction events and include them in the trace shown. Where does it start, where end? – usr Feb 21 '13 at 19:25
  • @usr Under the `Events Selection` tab, I had to hit the `[x] Show all events` checkbox to see anything transaction related. Then I checked everything under Transactions. When I profiled, I saw a bunch of "TransactionLog" entries with just a few "SQLTransaction" entries, but none of them had anything helpful in the TextData. I would expect to see "TM: Begin Tran starting" but did not... why would this happen? Do I have to install transaction support somewhere? – Langdon Feb 21 '13 at 19:32
  • 1
    I repro'd this on my machine. This means that you are not generating transactions (you are using implicit transactions per-statement). Find out why the TScope did not take. Try putting it outside the new entities. Note, that if the TScope does not take no warning is generated. It just silently does nothing. – usr Feb 21 '13 at 19:57
  • @usr repro'd it, meaning it rolls back for you? I took out the try/catch and put the TransactionScope around the entire method, but still no luck. I'm going to try it in a new solution and see what happens. I'm at a loss... – Langdon Feb 21 '13 at 20:13
  • 1
    repro = I saw what you saw.; Does your conn str contain Enlist=false? Why doesn't the context enlist into the transaction? Try manually opening the connection and manually enlisting it into Transaction.Current (I forgot how this is done). – usr Feb 21 '13 at 20:31
  • @usr herp... my connection string sure did have `enlist=false;`. thanks for your help! – Langdon Feb 21 '13 at 22:17

1 Answers1

1

Pulling up my comments because they turned out to be answers:

Your observations mean that you are not generating transactions (you are using implicit transactions per-statement). Find out why the TScope did not take. Try putting it outside the new entities. Note, that if the TScope does not take no warning is generated. It just silently does nothing.

Does your conn str contain Enlist=false?

usr
  • 168,620
  • 35
  • 240
  • 369