1

I'm in need of the functionality to retrieve a tracked entity by key without a SQL call to the DB. So I need something like the .Find but if it's not tracked NULL is returned.

The reason why I need this is because following code throws:

public void Test()
{
    var id = 5;

    var a = new Entity { Id = id };
    _ctx.Entry(a).State = EntityState.Added;

    var b = new Entity { Id = id };
    var bEntry = _ctx.Entry(b);

    // Delete entity
    // When it was already added in this 'lifetime' detach it again (since it's not saved in the DB yet)
    if (bEntry.State == EntityState.Added)
        bEntry.State = EntityState.Detached;
    else
        bEntry.State = EntityState.Deleted;
    
    var c = new Entity { Id = id };
    var cEntry = _ctx.Entry(c);
    
    // Add entity
    // When it was already deleted in this 'lifetime' detach it again (since it's not removed in the DB yet)
    if (cEntry.State == EntityState.Deleted)
        cEntry.State = EntityState.Detached;
    else
        cEntry.State = EntityState.Added;
    
    var d = new Entity { Id = id };
    var dEntry = _ctx.Entry(d);
}

If you would never create new entities but always use a. This code will work. The reason why it doesn't work now, is because internally EF tracks the entities in a dictionary using a 'by ref compare' (https://github.com/dotnet/efcore/blob/9ac01d6035c76626d89aa1a3cd8d200db2c3c0e1/src/EFCore/ChangeTracking/Internal/EntityReferenceMap.cs#L103)

As a result the TryGetValue EF uses will be false and it will create a new entry with state Detached. And later the code will crash because I try to add a entity that is already tracked.

This above problem would be solved if I could get the tracked entity since they would have the same ref. But I don't seem to find a way to access the same code that happens in .Find(). I could in theory use the .Local property or get it by using ChangeTracker.Entries() but since this are just regular lists the performance to lookup the entry is taking way to long.

I know this is a very specific question. But I don't really seem to find anything. It's a bit a shame to give up since internally EF knows what I need.

Brecht

ErazerBrecht
  • 1,583
  • 2
  • 20
  • 37
  • I'm confused. "reason why I need this is because following unit", and you know the answer: "use the .Local property". In a unit test why is the lookup "taking way too long"? And have you considered using the In-Memory provider for testing? https://learn.microsoft.com/en-us/ef/core/testing/#:~:text=Instead%20we%20use%20the%20EF%20in-memory%20database%20when,database%2C%20as%20well%20as%20SQL%20Server%20and%20SQLite. – David Browne - Microsoft Aug 12 '21 at 17:50
  • @DavidBrowne-Microsoft I didn't want to explain the whole reason why I need this since it would be to offtopic for this question. But I have about 100K tracked entities. I created this test to reproduce the problem I have 'in the real implemantion'. If you are interested in why I need such a system: https://gist.github.com/ErazerBrecht/b5808fc43c63e9f1d37fedb0ff651c10 – ErazerBrecht Aug 12 '21 at 17:54
  • Then I would add the tracked entities to a `Dictionary` that mirrors the change tracker. – David Browne - Microsoft Aug 12 '21 at 17:56
  • @DavidBrowne-Microsoft I first want to thank you for your time! This was indeed my backup plan but I was secretly hoping there would be a way to access the one inside EF. – ErazerBrecht Aug 12 '21 at 17:59
  • EF is for fetching things from a database. This code is basically asking why when you take 2 wheels off a car it's not suddenly a motorbike. This is not what EF is designed for really, *and* it still allows you to do it with `.local` anyway.. There are other ways to maintain an in-memory cache of things; what is your actual problem, and why is looking it up in a list too slow? I'm sure EF has plenty of unit tests of its own without you adding your own... – GPW Aug 12 '21 at 17:59
  • @GPW I do agree in a way. I placed the reason why in a gist above. But you could say the same about the auto improvement to 'cache' inside Find(). There are a lot of tracked entities which makes the list too slow. The unit test was a mistake it was just for me to reproduce the problem I'm having. (It's not really inside my code) – ErazerBrecht Aug 12 '21 at 18:02
  • Note that .Local supports the CollectionChanged event to make mirroring simpler. – David Browne - Microsoft Aug 12 '21 at 18:13
  • @DavidBrowne-Microsoft Thx for that tip! Will consider it for sure! – ErazerBrecht Aug 12 '21 at 18:14
  • Not so very effective but to get idea: https://stackoverflow.com/a/65110262/10646316 – Svyatoslav Danyliv Aug 12 '21 at 19:36
  • 1
    The fact that they don't expose this is bothering me for a long time. In my answer to the post marked as duplicate you'll find a code with uses the exact mechanism as `Find` method under "internal API" usage warning. – Ivan Stoev Aug 13 '21 at 03:36
  • 1
    @IvanStoev It's not the first time you provide me with an answer! Thank you very much! the EF and SQL questions on SO wouldn't be the same without you! Enjoy your day – ErazerBrecht Aug 13 '21 at 06:59

0 Answers0