0

I'm working with an existing set of code that manages multiple database updates in a single transaction. Here is a simplified example:

Database db = DatabaseFactory.CreateDatabase();
using (DbConnection dbConnection = db.CreateConnection())
{
    dbConnection.Open();
    DbTransaction dbTransaction = dbConnection.BeginTransaction();
    try
    {
        //do work
        dbTransaction.Commit();
    }
    catch (Exception ex)
    {
        dbTransaction.Rollback();
    }
}

I am also using EntityFramework in this same project for new development. Below is a simplified example of the usage of my repository class:

List<ThingViewModel> things = new List<ThingViewModel>();
// populate list of things
IObjectRepository thingRepository = new ThingRepository();
thingRepository.AddThings(things);
thingRepository.Save();

I want the work done in 'AddThings' to happen as part of the transaction in the first block of code.

Is there some clean way of blending my repository pattern into this existing code or vice-versa? I'm not at the point yet where it is feasible to rewrite the existing code to be entirely within EntityFramework, so I'm looking for some some interim approach.

I have tried passing the transaction from the older code into the repository, and thus EntityFramework, but that does not seem to work. I have also tried passing the ObjectContext back out to the older code in order to enlist it in the transaction. Neither approach works.

I cannot believe that I am the first person to encounter this hurdle in migrating existing code to EntityFramework... there must be something I am not considering.

I'll list the things that I have tried below:

using (TransactionScope transactionScope = new TransactionScope())
{   
    Database db = DatabaseFactory.CreateDatabase();
    using (DbConnection dbConnection = db.CreateConnection())
    {
        dbConnection.Open();
        DbTransaction dbTransaction = dbConnection.BeginTransaction();
        try
        {
            //do work
            dbTransaction.Commit();
        }
        catch (Exception ex)
        {
            dbTransaction.Rollback();
        }
    }

    Thing thing = new Thing(){
        Prop1 = Val1,
        Prop2 = Val2
    };
    ThingObjectContext context = new ThingObjectContext();
    context.Things.AddObject(thing);
    context.SaveChanges();
    transactionScope.Complete();
}

This last example 'works', it does not function as a transaction. When the EF insert fails, the EL commands are not rolled back by the TransactionScope. If I don't put those explicit calls to .Commit() and .SaveChanges(), nothing happens. I would really like for this to share the same connection if possible. Two other variations of this I am currently playing around with is trying to use the same connection between both EF and EL as well as use EnlistTransaction on one side or the other. Definitely trying to keep this from becoming a MSDTC - don't want the extra overhead associated with that.

DMC
  • 361
  • 4
  • 15

2 Answers2

2

Use TransactionScope instead of explicit transaction management. You'll simplify the overall code and everything you do should automatically detect and use the same transaction.

Chris Tavares
  • 29,165
  • 4
  • 46
  • 63
  • Thanks for your input - I updated my original post with what I think you were suggesting. Still playing around with several different variations but not having any success. I can get either EL or EF to work but not together in the same transaction. – DMC Jun 21 '12 at 17:46
  • What if you remove the explicit DbTransaction object completely? Actually, and remove the explicit creation of the connection completely as well - Entlib will open & close the connection automatically for you, and use the transactionscope as the transaction boundary as well. – Chris Tavares Jun 21 '12 at 23:40
  • The issue is that the code in EL is part of a common pattern used across all business objects and thus it is EVERYWHERE. I'm looking for some solution that will allow us to blend the EL code with new EF code for a short period of time. It doesn't look like it is possible, though. – DMC Jun 21 '12 at 23:59
1

Is there any way you can call Database.GetOpenConnection() instead of CreateConnection() in your EL code, and pass in the things.Connection that you create inside of a TransactionScope block? I haven't tested this, but that is what I would try first.

Alan McBee
  • 4,202
  • 3
  • 33
  • 38
  • If you could post some code snippet that would be awesome. Currently I can't find a way to reference another connection with EL like you are suggesting with 'GetOpenConnection()', and it doesn't seem like the connection types between EL and EF are compatible (DbConnection v. EntityConnection). – DMC Jun 21 '12 at 14:59
  • same goes for the transaction types the two use (DbTransaction v. Transaction) – DMC Jun 21 '12 at 15:11
  • Sorry, I couldn't figure a way to make this work as easily as I thought. Looks like you're headed towards MSDTC-ville. – Alan McBee Jun 21 '12 at 21:08