1

According to Entity Framework documents, Entity Framework will only cache data when using .Find() to query data.

In the following sample I got query data cached when using .First():

  static void Main(string[] args)
    {
        var trxScope = new TransactionScope();
        var context = new SekhavatDBEntities();

        //the value of Balance is Zero
        var account =
            context.Accounts.First(acc => acc.Id == new Guid("79B7AAC6-AD2D-4824-907E-16ADB4C41EE1"));            
        account.Balance = 50;

        context.SaveChanges();
        trxScope.Dispose();

        var trxScope2 = new TransactionScope();
        var account2 =
            context.Accounts.First(acc => acc.Id == new Guid("79B7AAC6-AD2D-4824-907E-16ADB4C41EE1"));

        //Balance of account2 Is 50 Now!
        trxScope2.Dispose();
    }

Why is account2's Balance 50? According to my considerations, it should be 0.

Thanks in advance

Antoine
  • 352
  • 1
  • 10
  • 18
M.Azad
  • 3,673
  • 8
  • 47
  • 77
  • `account.Balance = 50;` `context.Entry(account).State = EntityState.Modified;` `context.SaveChanges();` – Celso Lívero Dec 04 '17 at 16:59
  • You saved the changes, why wouldn't it be 50? – juharr Dec 04 '17 at 16:59
  • 1
    @juharr because i disposed the transaction and the transaction rolled back, therefor in database the balance is 0 – M.Azad Dec 04 '17 at 17:01
  • this is not how it should be done to give itself ROLLBACK – Celso Lívero Dec 04 '17 at 17:03
  • 1
    @CelsoLívero record in database is really rolled back, and context should not cach data when using .First(), Therefore in the Next .First() it should Query Data Again and get rolled back data, but it doesnt Query data and returned cached account – M.Azad Dec 04 '17 at 17:04
  • [Why you should not use Entity Framework with Transactions] (https://coderwall.com/p/jnniww/why-you-shouldn-t-use-entity-framework-with-transactions) [Entity Framework Working with Transactions (EF6 Onwards)](https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx) – Celso Lívero Dec 04 '17 at 17:08
  • [Entity Framework 4 Single() vs First() vs FirstOrDefault()](https://stackoverflow.com/q/3485317/5605739) – Celso Lívero Dec 04 '17 at 17:14
  • That's expected, because there is already entity with the same key tracked by context. It will make a query (can confirm by logging queries). Then it will see there is already entity with the same key in context, discard query results and return the same entity (can confirm by comparing account and account2 with object.equals). Solution - don't reuse the same context for multiple transactions or use AsNoTracking. – Evk Dec 04 '17 at 17:31
  • 1
    @M.Azad Can you post a link to the documentation saying that the data is **only** cached when using `Find`? Because that's simply not true. – Ivan Stoev Dec 04 '17 at 18:52
  • Your First condition has the same ID in per sections. – Jahed Kabiri Dec 05 '17 at 17:51

3 Answers3

3

Here is relevant quote from documentation:

Note that DbSet and IDbSet always create queries against the database and will always involve a round trip to the database even if the entities returned already exist in the context

...

When results are returned from the database, objects that do not exist in the context are attached to the context. If an object is already in the context, the existing object is returned (the current and original values of the object's properties in the entry are not overwritten with database values).

Using transaction or not is not relevant. What is relevant is:

  1. EF will not change values back to original on transaction rollback (I don't think it's even possible for it to do that, but anyway). Doesn't matter if you use TransactionScope or ctx.Database.BeginTransaction.

  2. Your account with id 79B7AAC6-AD2D-4824-907E-16ADB4C41EE1 is already tracked by context by the time you make your second query. As per documentation above - query will be made against database but results of this query will be discarded and will not overwrite values of currently tracked entity. Instead, entity will be returned as is (with Balance equals to 50).

To prevent this either:

  1. Use separate contexts for separate operations and avoid reusing context as much as possible.

  2. Use AsNoTracking. This will prevent behavior described above and will always return entity as it is in database now.

  3. Use Reload:

     context.Entry(account).Reload();
    

This will query database for this entity and overwrite all values with current database values.

Community
  • 1
  • 1
Evk
  • 98,527
  • 8
  • 141
  • 191
1

Your TransactionScope must be inside your context and begin a transaction

you have no open transaction in your context if you declare them like you did

using information such as Celso Livero said => MSDN Page

using (var context = new SekhavatDBEntities()) 
{ 
    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
        var account = context.Accounts.First(acc => acc.Id == new Guid("79B7AAC6-AD2D-4824-907E-16ADB4C41EE1"));            
        account.Balance = 50;
        context.SaveChanges();

        dbContextTransaction.Rollback(); 
    } 
} 

use another context to check the data

using (var context = new SekhavatDBEntities()) 
{ 
    var account2 = context.Accounts.First(acc => acc.Id == new Guid("79B7AAC6-AD2D-4824-907E-16ADB4C41EE1"));
}

First() or Find() is not your problem with the transaction. and like Erk said, see there is already entity with the same key in context

the data is caching in your context, not because you are using First() or Find()

Gabriela
  • 11
  • 2
0

Use AsNoTracking()

//the value of Balance is Zero
        var account =
            context.Accounts.AsNoTracking().First(acc => acc.Id == new Guid("79B7AAC6-AD2D-4824-907E-16ADB4C41EE1"));
shop350
  • 198
  • 1
  • 6
  • 1
    the problem is, why context cached data when query using .First(), according to documents context should only cache data when querying using .Find() – Mehdi Dec 04 '17 at 17:39