简体   繁体   中英

EF6, injected dbContext caching issue

I have an EF ie EF6 project, in which I inject my DBContext and I have few LinQ query on it as shown.. GetUnSentMessages() brings cached data back, as when I change value in the database, it does not reflect in my query result.. Could you please suggest, where am I going wrong and what's the solution.. Thank you

update - GetUnSentMessages() picks up unsent messages.. UpdateMessageStatus() - is called after the unsent messages have been sent.

So if I go back and change any value for a record in the SMSMessage table, its not picked up by GetUnsentMessages query..It still bring back the old data..ie its caching things..

namespace SMSSender.Repositories
{
    public class SenderRepository : ISenderRepository
    {
        private SMSEntities entities;
        private ILog logger;

        public SenderRepository(SMSEntities entities, ILog logger)
        {
            this.entities = entities;
            this.logger = logger;
        }

        public IEnumerable<SMSMessage> GetUnSentMessages()
        {
            return entities.SMSMessage.Where(item => item.TimeSent == null && item.Deleted == 0 && item.StatusID == 0).ToList();
        }

        public void UpdateMessageStatus(int messageId, string mobileNo, short status)
        {
            var message = entities.SMSMessage.Where(item => item.MessageID == messageId && item.MobileNo == mobileNo).FirstOrDefault();
            if (message != null)
            {
                message.StatusID = status;
                message.TimeSent = DateTime.Now;
                entities.Entry(message).State = System.Data.Entity.EntityState.Modified;
                entities.SaveChanges();
            }
        }
    }
}

update 2 - Changed my code to this after @Jaroslav Surala suggested article and it works fine, not sure if its the best/right approach -

namespace SMSSender.Repositories
{
    public class SenderRepository : ISenderRepository
    {
        private SMSEntities entities;
        private ILog logger;

        public SenderRepository(SMSEntities entities, ILog logger)
        {
            this.entities = entities;
            this.logger = logger;
        }

        public IEnumerable<SMSMessage> GetUnSentMessages()
        {
            return entities.SMSMessage.AsNoTracking().Where(item => item.TimeSent == null && item.Deleted == 0 && item.StatusID == 0).AsNoTracking().ToList();
        }

        public void UpdateMessageStatus(int messageId, string mobileNo, short status)
        {
            var message = entities.SMSMessage.AsNoTracking().Where(item => item.MessageID == messageId && item.MobileNo == mobileNo).FirstOrDefault();
            if (message != null)
            {
                message.StatusID = status;
                message.TimeSent = DateTime.Now;
                entities.Set<SMSMessage>().AddOrUpdate(message);
                entities.SaveChanges();
            }
        }
    }
}

}

The problem is that EF6 use IdentityMap pattern. You must create new DB contetxt after update. Here http://codethug.com/2016/02/19/Entity-Framework-Cache-Busting/ is nice article about it.

The moment your DbContext is initialised, all subsequent changes that you have made in SSMS, as noted by your comments, will not be reflected, in that instance of your DbContext unless you do a refresh.

I hope the following code snippet can provide a more straightforward logical flow, that you can tweak to fit your application.

using(var SMSEntities = new SMSEntities()
{
    // Your first query
    var messages = entities.SMSMessage
        .Where(item => item.TimeSent == null && item.Deleted == 0 && item.StatusID == 0)
        .ToList();

    // To simulate whatever you're doing in SSMS here.
    LongRunningMethod();

    // At this point, even when you re-query, you will not be getting the updated values.
    messages = entities.SMSMessage
        .Where(item => item.TimeSent == null && item.Deleted == 0 && item.StatusID == 0)
        .ToList();

    // To get updated values, do this.
    SMSEntities.Entry(messages).Reload();
    // Messages here will be updated with what you did in SSMS.
    messages = entities.SMSMessage
        .Where(item => item.TimeSent == null && item.Deleted == 0 && item.StatusID == 0)
        .ToList();
}

The explanation is this, EF will still make a database query as usual. Now, after getting the results back from Database, EF realises that there is a list of messages with a set of IDs tied to your context( SMSEntities ) that you have obtained via your very first query. EF will instead return the old entities, which is what you perceived as caching.

You can reload to force a refresh or simply, new up a DbContext instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM