6

I would like to how to load/fetch the current database values for an entity ("force reload from database") with Entity Framework Core. My property looks like the following:

[Column(TypeName = "decimal(16, 2)")]
public decimal Fee { get; set; }

If the Fee is saved with a higher precision than the one specified in the Column Attribute e.g. 1,2888 the Database round it to two decimal placed but the entity I save does not get Updated.

So I tried to reload the values from the database to show the "correct current" values in this UI but neither of the following worked:

// removed from tracked entities and fetch from db
dbContext.Entry(entity).State = EntityState.Detached;
dbContext.Find(...);

// call reload
dbContext.Entry(entity).Reload();

Expected the value to be 1,29 after the refresh/reload but it always stays 1,2888. I have looked up the value in the database and it was 1,29 and also the next request would return 1,29 but I did not manage to return the correct value in the same request.

Is there a way to "force refresh" an Entity from the database?

--- Edit ---

The Problem was that I had an Entity with Navigation Properties and the decimal was on the navigation property which was not reloaded when calling .Reload() on the entity itself.

Code

using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace WebApplication1
{
    public class Entity
    {
        public long Id { get; set; }

        public Fee Fee { get; set; }
    }

    public class Fee
    {
        public long Id { get; set; }

        [Column(TypeName = "decimal(16, 2)")]
        public decimal Value { get; set; }
    }


    public class MyContext : DbContext
    {
        public DbSet<Entity> Entities { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer(@"connectionString");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
        }
    }

    public class Program
    {
        public static void Main()
        {
            AsyncMethod().GetAwaiter().GetResult();
        }

        private static async Task AsyncMethod()
        {
            using (var context = new MyContext())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();
            }

            using (var context = new MyContext())
            {
                var entity = new Entity {Fee = new Fee {Value = 12.3456789m}};
                context.Add(entity);
                await context.SaveChangesAsync();
                Console.WriteLine($"Fee value after SaveChanges() {entity.Fee.Value}");
                await context.Entry(entity).ReloadAsync();
                Console.WriteLine($"Fee value after Reload() {entity.Fee.Value}");
            }

            using (var context = new MyContext())
            {
                var entity = await context.Entities.OrderByDescending(x => x.Id).Include(x => x.Fee).FirstAsync();
                Console.WriteLine($"Fee value after Disposing and Recreating Context {entity.Fee.Value}");
            }

        }
    }
}

Output:

Fee value after SaveChanges() 12,3456789
Fee value after Reload() 12,3456789
Fee value after Disposing and Recreating Context 12,35
Edub
  • 456
  • 6
  • 17
  • EF Core doesn't cache anything. Instead of trying to "update" the same entity object just *load* the data again and bind your UI to the new data. – Panagiotis Kanavos Aug 10 '18 at 11:27
  • Actually I am providing an API and the UI fetches the data from this API. My use case is that I want to patch a model and return the "current values". So I have a single DbContext for this Request and as far as I know the EF Core loads data only once for this entity (Identity Map Pattern - https://stackoverflow.com/a/3653392/732846). – Edub Aug 10 '18 at 11:48
  • no, EF Core always does what you tell it to. It doesn't cache or "patch" entities, it loads them every time you tell it to. The entities it loads each time are *different* even if the same ID was used to load them. – Panagiotis Kanavos Aug 10 '18 at 11:53
  • 6
    @PanagiotisKanavos That's definitely not true. EF Core returns the same instance (with the cached data) if the entity is tracked, even if it queries the database first. Basically the client wins strategy. – Ivan Stoev Aug 10 '18 at 11:58
  • @IvanStoev probably - I never use long-lived contexts such reasons. And I had to debug quite a lot of blocking, deadlock cases where someone used a single context *and* long lived transactions – Panagiotis Kanavos Aug 10 '18 at 12:02
  • @Edub `Reload` works for me (EF Core 2.1.1). What EF Core version are you on? And hope you call `Reload` after `SaveChanges` and no external transaction. – Ivan Stoev Aug 10 '18 at 12:07
  • I am using 2.1.1 as well and it Reload usually works. But in this particular case with the precision of the decimal being rounded and truncated by the Database/Framework the Reload() seems not to return the rounded/truncated Database value. It might even be better to validate the model before giving it to the DataModel but I was wondering what is wrong here. – Edub Aug 10 '18 at 12:13
  • 1
    Just set up an Empty Project with only one Class and tried to reproduce it. In this project it works as expected. Thanks so far - I need to reproduce it to give a better description. Fee value after SaveChanges() 12,3456789 Fee value after Reload() 12,35 Fee value after Disposing and Recreating Context 12,35 – Edub Aug 10 '18 at 12:35
  • +1 That ReloadAsync() command has just saved my life. I was desperately trying to get EF Core to reload a record from our database, after an API had updated it, but it kept returning the cached version. Using ReloadAsync() or setting dbContext.ChangeTracker.QueryTrackingBehavior to NoTracking fixed this issue for me. But it's taken DAYS for me to track down this issue. – Mike Gledhill Nov 01 '18 at 09:42

1 Answers1

0

Possible solution in limited situations

Sometimes AsNoTracking can be a solution if you just need to load data and not maintain a whole context of objects. https://docs.microsoft.com/en-us/ef/core/querying/tracking

For instance I just had a case where I needed to :

  • Load a single row
  • Start off a hangfire task (running in its own context)
  • Check the status after a few seconds in the original method

By using AsNoTracking I was able to load the same row twice and not see anything cached.

var shipmentData = await context.Shipments.AsNoTracking().Single(s => s.ShipmentId == shipmentId);

HOWEVER If you actually need to save this row back this method can get confusing to manage or risk losing changes.

Simon_Weaver
  • 120,240
  • 73
  • 577
  • 618