Ramesh Rajendran
Ramesh Rajendran

Reputation: 38703

Entity Framework Caching Issue

I am new to Entity Framework.

I have get to some values in my database using EF. It returns perfectly, and the values are shown in labels. But When I delete all values in my table (without using EF), the EF query is returning my old values. I know the EF stores the values in cache and returns the cached data for subsequent runs. Is this correct?

So how can I solve the problem when I have deleted all values in my database, but EF returns the old values?

Edit:

Now i used datamodel.SaveChanges(). But now also it's returning the same old values.

My sample query is look like below:

SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities();
datamodel.SaveChanges();
List<Compliance> compliance=new List<Compliance>();
IList<ComplianceModel> complianceModel;
if (HttpContext.Current.User.IsInRole("SuperAdmin"))
{
    compliance = datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList();
}

Upvotes: 64

Views: 87372

Answers (13)

Dave Alperovich
Dave Alperovich

Reputation: 32510

If you know that changes happened outside of EF and want to refresh your ctxt for a specific entity, you can call ObjectContext.Refresh

datamodel.Refresh(RefreshMode.StoreWins, orders);

If this seems like it will be a common occurrence, you should disable object caching in your queries:

SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities();
datamodel.tblCities.MergeOption = MergeOption.NoTracking; 

or to turn off object level caching for specific Entity:

Context.Set<Compliances>().AsNoTracking();

Upvotes: 45

3263927 contra
3263927 contra

Reputation: 29

context.ChangeTracker.Clear(); 

called just before reload data call helped. i can`t recreate dbcontext because i use some default settings in the DI pipeline, also i need tracking, but if you reset tracking before first call it will help to track what changed after, but disable caching just before

my method look like this:

public async Task<Data> GetData(int dataId)
{
    context.ChangeTracker.Clear();
    Data c = await context.Datas.Include(x => x.FieldStates).Where(x => x.Id == dataId).SingleAsync();
    return c;
}

Upvotes: 1

user3444999
user3444999

Reputation: 571

As of July 27, 2023 - This Worked for me, I was facing the same caching issue :

context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

Source : https://learn.microsoft.com/en-us/ef/core/querying/tracking

Upvotes: 0

Tiago Gouv&#234;a
Tiago Gouv&#234;a

Reputation: 16800

I recommend you to use some MergeOption to all EntitieSet after create the context, like this:

var objSetProps = ctx.GetType().GetProperties().Where(prop => prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(ObjectSet<>));
foreach (PropertyInfo objSetProp in objSetProps)
{
    ObjectQuery objSet = (ObjectQuery)objSetProp.GetValue(ctx, BindingFlags.GetProperty, null, null, null);
    objSet.MergeOption = MergeOption.PreserveChanges;
}

Read about the MergeOption here: http://msdn.microsoft.com/en-us/library/system.data.objects.mergeoption.aspx Your will use NoTracking, I think.

If you want to CLEAR the "cached" entities, detaching it.

var entidades = Ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged);
foreach (var objectStateEntry in entidades)
    Ctx.Detach(objectStateEntry.Entity);

Where Ctx are my Context.

Upvotes: 5

Jin Thakur
Jin Thakur

Reputation: 2783

EF works differently with find method which gives data from context .Rest other query are from db.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). A query is executed against the database when:

Microsoft link

It is enumerated by a foreach (C#) or For Each (Visual Basic) statement. It is enumerated by a collection operation such as ToArray, ToDictionary, or ToList. LINQ operators such as First or Any are specified in the outermost part of the query. The following methods are called: the Load extension method on a DbSet, DbEntityEntry.Reload, and Database.ExecuteSqlCommand. 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).

When you perform a query, entities that have been added to the context but have not yet been saved to the database are not returned as part of the result set. To get the data that is in the context, see Local Data.

If a query returns no rows from the database, the result will be an empty collection, rather than null.

Upvotes: 0

Michael
Michael

Reputation: 1183

In my case it was a bad connection string. Entity looked like it was doing great because it never complained until I made the context local and it finally gave me the error message.

Upvotes: 0

David Sherret
David Sherret

Reputation: 106840

I think you should follow some of the other solutions here, but it seems like you're wanting to clear the cache. You can achieve this by doing the following:

var count = datamodel.Compliances.Local.Count; // number of items in cache (ex. 30)

datamodel.Compliances.Local.ToList().ForEach(c => {
    datamodel.Entry(c).State = EntityState.Detached;
});

count = datamodel.Compliances.Local.Count; // 0

Upvotes: 9

Adil Mammadov
Adil Mammadov

Reputation: 8706

I think what you need is GetDatabaseValues(). It is used like:

context.Entry(/*your entry*/).GetDatabaseValues();

Information below is from msdn:

The current values are the values that the properties of the entity currently contain. The original values are the values that were read from the database when the entity was queried. The database values are the values as they are currently stored in the database. Getting the database values is useful when the values in the database may have changed since the entity was queried such as when a concurrent edit to the database has been made by another user.

Upvotes: 3

Marc Johnston
Marc Johnston

Reputation: 1286

Couple things you can do.

  1. Use a new context. The cached entities are stored in the context. Using a new context prevents it from using the cache.
  2. If you really want a global/long lasting context, you have two sub options: a.) always call the Reload method. db.Entry(entity).Reload() ... this forces the context to reload that entity. b.) use a SqlDependency object to detect when records change and reload the entities as needed. https://code.msdn.microsoft.com/How-to-use-SqlDependency-5c0da0b3

Upvotes: 1

Colin
Colin

Reputation: 22595

I suspect that the underlying problem here is that your DbContext is hanging around too long. I can tell from the fact that you are using HttpContext that you have a web application, and General guidelines when working with DbContext include

When working with Web applications, use a context instance per request.

If you are using MVC you could use the Dispose pattern in your controller like this:

public class EmployeeController : Controller
{
    private EmployeeContext _context;

    public EmployeeController()
    {
        _context = new EmployeeContext();
    }

    public ActionResult Index()
    {
        return View(_context.Employees.ToList());
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _context.Dispose();
        }
        base.Dispose(disposing);
    }
}

But you really ought to be looking at dependency injection to manage the DbContext lifetime

Upvotes: 2

Brian from state farm
Brian from state farm

Reputation: 2896

Firstly I would not suggest modifying the database external to your system unless you are only doing testing and development.

The EF DbContext contains an IDisposable interface. To release any cached data either make the Dispose calls manually or place your Database Object inside a using block.

        using (SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities())
        {
            List<Compliance> compliance = new List<Compliance>();
            IList<ComplianceModel> complianceModel;
            if (HttpContext.Current.User.IsInRole("SuperAdmin"))
            {
                compliance = datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList();
            }
        }

This will make sure the context is cleared and recreated the next time it is used. Make sure to do this for all your calls and not just the one you are having issues with.

Upvotes: 2

HGMamaci
HGMamaci

Reputation: 1396

Below code helped my object to be refreshed with fresh database values. The Entry(object).Reload() command forces the object to recall database values

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();

Upvotes: 3

Akash Kava
Akash Kava

Reputation: 39956

EF will not load changes unless you re query the context. EF queries db and loads maps them into objects, it watches changes you perform on objects and not on the database. EF does not track changes made directly to database and it will never track.

You have loaded a List, that List is your cache in memory. Even calling Save Changes will not refresh. You will have to query the context once again, that is create new list.

To see changes You will have to execute following line once more,

datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList()

Upvotes: 19

Related Questions