4

In this code

using (var db = new DbPerson())
{
    var b = db.People.Create();
    b.Name = "Homer";

    db.People.Add( b );

    Console.WriteLine( "Count: {0}", db.People.Count() );

    foreach (var bb in db.People)
        Console.WriteLine( bb.Name );

    var fb = db.People.Find( b.Id ); // Id is a GUID generated in the Person ctor
                                     //  NOT a DB-generated Identity.
    Console.WriteLine( "Found: {0}", fb != null );

    db.SaveChanges();

    Console.WriteLine( "Count: {0}", db.People.Count() );
}

the output looks like this:

Count: 0
Found: True
Count: 1

I have seen other posts about Count not being updated until SaveChanges is called. OK, so this is the "way it works".

My question is specifically this: Why does Find return an object from db.People when Count() returns 0 and the enumerator returns no elements? Shouldn't Find and Count() act similarly, waiting for SaveChanges before returning Entities that are in the "Added" state?

What is the reasoning for this? I ask because I am writing a light-weight non-SQL provider that needs to mirror the actions of EF as nearly as possible. I can't figure out what logic causes Find to return an Added element that Count() and GetEnumerator() doesn't. I need to address these situations.

Veldaeven
  • 319
  • 1
  • 13

2 Answers2

1

Find() has a special quality: it will first look into the DbContext's entities that have been newly added but not yet flushed to the database (e.g.: entities with EntityState.Added).

Count() and the enumerator will instead look at the database, which obviously doesn't have anything yet until you call SaveChanges().

Background info

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
  • I completely understand this. My question is Why, and where else do I need to know that the context's newly added entities are searched/included and where are they not? – Veldaeven Jun 10 '15 at 17:39
  • 1
    Each time you are getting unexpected results, I assume? For example this was also explained in the [docs](https://msdn.microsoft.com/en-us/library/gg696418(v=vs.113).aspx): "*If an entity with the given primary key values exists in the context, then it is returned immediately without making a request to the store.*" – Jeroen Vannevel Jun 10 '15 at 17:42
  • After reading the docs on every method on `DbSet<>`, as well as testing things such as using `Queryable.FirstOrDefaulit` on a `DbSet`, I think that's the answer- The Find method is the only place where it states explicitly that the return values are from the context, not the database. `FirstOrDefault` will not "find" the entity until after the Save Changes. – Veldaeven Jun 10 '15 at 18:16
1

If you ask why it is implemented like this: Find is supposed to allow you to access tracked entities by ID that are not in the database. Find is a rather special purpose method. Maybe it allows to retrieve deleted entities as well, the docs don't say. There are other APIs for reaching into the pool of tracked entities as well.

Usually it is a sign of bad use of EF (or a sign of a hack) if you need to mess with that.

NHibernate had a really useful Find: It allowed you to get an entity without going to the database. It returns a proxy. That way you always could pass around entity objects and almost never IDs. A nice abstraction. This is not possible with EF. Find goes to the database.

Community
  • 1
  • 1
usr
  • 168,620
  • 35
  • 240
  • 369
  • I just tested it- An entity that has been Removed (before SaveChanges, in the "Deleted" state) will also be returned by Find. – Veldaeven Jun 10 '15 at 18:14
  • Btw, NHibernate was nice in another regard. It automatically saved all changes before each query. That way the database was always in sync and it was hard to tell the difference between context results and db results. The motto of NHibernate was persistence ignorance. – usr Jun 10 '15 at 20:32