web-dev-qa-db-fra.com

Utilisation de TransactionScope avec Entity Framework 6

Ce que je ne peux pas comprendre, c'est s'il est possible d'apporter des modifications au contexte et d'obtenir les modifications dans la même transaction avant qu'elle ne soit validée.

Voici ce que je recherche:

using (var scope = new TransactionScope(TransactionScopeOption.Required)) 
{ 
    using (var context = new DbContext()) 
    { 
        //first I want to update an item in the context, not to the db
        Item thisItem = context.Items.First();
        thisItem.Name = "Update name";
        context.SaveChanges(); //Save change to this context

        //then I want to do a query on the updated item on the current context, not against the db
        Item thisUpdatedItem = context.Items.Where(a=>a.Name == "Update name").First();

        //do some more query
    } 

    //First here I want it to commit all the changes in the current context to the db
    scope.Complete(); 
} 

Quelqu'un peut-il m'aider à comprendre et à me montrer un schéma de travail?

20
Marcus Höglund

Oui, c'est possible et c'est très utile lorsque vous voulez insérer une entité dans la base de données et utiliser l'id généré automatiquement pour la prochaine insertion ou mise à jour

using (var context = new DbContext())     
{ 
    using (var transaction = context.Database.BeginTransaction()) {
        var item = new Item();
        context.Items.Insert(item);
        context.SaveChanges(); // temporary insert to db to get back the auto-generated id

        // do some other things
        var otherItem = context.OtherItems.First();
        // use the inserted id
        otherItem.Message = $"You just insert item with id = {item.Id} to database";
        transaction.Commit();
    }
} 

Parce que votre question portait également sur un modèle de travail, voici mon code de travail (avec l'utilisation de FluentApi, DbContext & Transaction). J'avais le même problème que vous :). J'espère que cela vous aide

public class FluentUnitOfWork : IDisposable
{
    private DbContext Context { get; }

    private DbContextTransaction Transaction { get; set; }

    public FluentUnitOfWork(DbContext context)
    {
        Context = context;
    }

    public FluentUnitOfWork BeginTransaction()
    {
        Transaction = Context.Database.BeginTransaction();
        return this;
    }

    public FluentUnitOfWork DoInsert<TEntity>(TEntity entity) where TEntity : class
    {
        Context.Set<TEntity>().Add(entity);
        return this;
    }

    public FluentUnitOfWork DoInsert<TEntity>(TEntity entity, out TEntity inserted) where TEntity : class
    {
        inserted = Context.Set<TEntity>().Add(entity);
        return this;
    }

    public FluentUnitOfWork DoUpdate<TEntity>(TEntity entity) where TEntity : class
    {
        Context.Entry(entity).State = EntityState.Modified;
        return this;
    }

    public FluentUnitOfWork SaveAndContinue()
    {
        try
        {
            Context.SaveChanges();
        }
        catch (DbEntityValidationException dbEx)
        {
            // add your exception handling code here
        }
        return this;
    }

    public bool EndTransaction()
    {
        try
        {
            Context.SaveChanges();
            Transaction.Commit();
        }
        catch (DbEntityValidationException dbEx)
        {
            // add your exception handling code here
        }
        return true;
    }

    public void RollBack()
    {
        Transaction.Rollback();
        Dispose();
    }

    public void Dispose()
    {
        Transaction?.Dispose();
        Context?.Dispose();
    }
}

Exemple d'utilisation:

var status = BeginTransaction()
                // First Part
                .DoInsert(entity1)
                .DoInsert(entity2)
                .DoInsert(entity3)
                .DoInsert(entity4)
                .SaveAndContinue()
                // Second Part
                .DoInsert(statusMessage.SetPropertyValue(message => message.Message, $"Just got new message {entity1.Name}"))
            .EndTransaction();
40
Kien Chu

Si vous voulez vous assurer que vous interrogez uniquement le contenu local de votre contexte, vous pouvez utiliser la collection "locale":

Item thisItem = context.Items.First();  
thisItem.Name = "Update name";    
Item thisUpdatedItem = context.Items.Local.Where(a=>a.Name == "Update name").First();

Cela interrogera uniquement les données en mémoire du contexte et n'atteindra pas la base de données.
Les données "locales" sont présentes dès que vous matérialisez un objet dans le contexte en l'ajoutant ou en le chargeant à partir de la base de données, c'est-à-dire que vous n'avez pas besoin d'appeler SaveChanges ().
SaveChanges () écrira le contenu du contexte dans votre base de données.

3
Stephan Keller