0

The Problem:

No matter how sure I am that my transactions are committed and the application can read the absolute latest from the database, sometimes when I refresh the application the changes aren't displaying the latest data and I suspect that the data is being cached in the browser! This suspicion comes from loading the web application in another browser after the changes are made and seeing the changes. I need to make it so that every time the page is refreshed, this data is not cached in the browser.


Setup:

One Web Application simply reads from the database, while AJAX calls are made client side to a REST API that adds, deletes, and updates the data.

I have been doing a lot of research and the most valuable resource I have found on this was here: http://mehdi.me/ambient-dbcontext-in-ef6/

My code that reads from the database uses this pattern:

public IEnumerable<Link> GetLinks(){
    using (var context = new MyContext()){
         foreach(var link in context.ChangeTracker.Entries())
         {
             link.Reload();
         }
         return context.Links.Where(x => x.UserId == this.UserId).ToList();
    }
}

An example of one of my operations that reads follows this pattern:

    public int AddLink(string text, string url)
    {
        using (var context = new MyContext())
        {
            Link linkresult;
            using (var contextTransaction = context.Database.BeginTransaction())
            {
                var link = new Link()
                {
                    Text = text,
                    Url = url
                    UserId = this.UserId
                };

                linkresult = context.Links.Add(link);
                context.SaveChanges();
                contextTransaction.Commit();
            }
            return linkresult.Id;
        }
    }

Now as shown above, the context.SaveChanges() with the contextTransaction.Commit() I'm making sure that the data gets written to the database and is not cached at any level. I have confirmed this by using the Server Explorer and watching the content get updated real time.

I also think I have confirmed that my read will pull up the latest information from the database by loading the web application in another browser after the changes have been made, but I acknowledge that this may also be a caching issue that I am not aware of.

My last step is getting around the caching that happens in the browser. I know chrome allows you to clear your app hosted data, but I don't know how to make certain data is not cached so that every time a request happens this code executes.


More Details on the REST API: The Controller for the above example looks something nearly identical to this:

    public ActionResult AddLink(MyLink model)
    {
        IntegrationManager manager = new IntegrationManager(System.Web.HttpContext.Current.User);
        model.Id = manager.AddLink(model.Text, model.Url);
        return Json(model);
    }

The IntegrationManager is just a basic class that does not implement IDisposable because the context is created and disposed of during each transaction. As can be seen, the AddLink is a member of the IntegrationManager class.


More Details on the Web Application: The model for the view creates an IntegrationManager in it's constructor as a temporary variable to make the getLinks call as follows:

    public Home(IPrincipal user, Cache cache)
    {
        this.HttpCache = cache;

        IntegrationManager _IntegrationManager = new IntegrationManager(user);
        this.Links = this.GetLinks(_IntegrationManager);

    }

AJAX Call:

          .on("click", "#btn-add-link", function (event) {
                var text = $("#add-link-text"),
                    url = $("#add-link-url");

                if (text.val().length > 0 && url.val().length > 0) {
                    var hasHttp = /^http.*$/.test(url.val());
                    if (!hasHttp) {
                        url.val("http://" + url.val());
                    }

                    $.ajax({
                        url: addLinkUrl,
                        type: "POST",
                        data: { Text: text.val(), Url: url.val() }
                    }).success(function (data) {
                        var newLink = $('<li class="ui-state-default deletable" id="link-' + data.Id + '"><a href="' + data.Url + '" target="_blank">' + data.Text + '</a></li>');

                        $("#user-links").append(newLink);

                        text.val("");
                        url.val("");

                    });
War Gravy
  • 1,153
  • 2
  • 14
  • 25
  • Can you post your view and or controller code? – esmoore68 Mar 31 '16 at 17:16
  • @esmoore68 sure, it will take a few minutes. – War Gravy Mar 31 '16 at 17:17
  • 1
    If its an async Http GET call that you are doing then that could very well be. You can specify how long the content is good for in the header OR even apply a random name/value pair in the URL. This will tell the browser that the content IS different and a trip to the server is required. There is also a property in JQuery I believe that you can set to make sure that the browser does not rely on the cache for AJAX Get calls but which browser and versions actually adhere to that setting is not always well documented. – Igor Mar 31 '16 at 17:19
  • 1
    This previous SO question [Prevent browser caching of jQuery AJAX call result](http://stackoverflow.com/questions/367786/prevent-browser-caching-of-jquery-ajax-call-result) might solve your problem. – Igor Mar 31 '16 at 17:23
  • esmoore68 and Igor I have added more details on the server side code for the rest api and the web app. I am looking into that previous question now. – War Gravy Mar 31 '16 at 17:27
  • @Igor thank you for that question, but I don't think it is the same issue I'm running into. I have posted the AJAX now as well. – War Gravy Mar 31 '16 at 17:32
  • Open the browser's network monitor and watch the requests -- you should be able to tell if the content is cached right away. GET requests _may_ be cached by the browser even if the headers indicate otherwise which is why [url cache-busting techniques](http://stackoverflow.com/questions/367786/prevent-browser-caching-of-jquery-ajax-call-result) are necessary. While your question contains a lot of code it's hard to tell what your app is doing and what action your AJAX is calling. Are your actions `[HttpPost]` or `[HttpGet]`? Where's the code that show your stale data? – Jasen Mar 31 '16 at 17:53

1 Answers1

0

Okay, so I have found out how to make sure no caching happens. The following is an attribute for the controller of the web application called NoCache. To use it, your controller will need the attribute like this:

using whatever.namespace.nocache.lives.in

[NoCache]

Here is the details of the attribute:

public class NoCacheAttribute : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
        filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
        filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
        filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        filterContext.HttpContext.Response.Cache.SetNoStore();

        base.OnResultExecuting(filterContext);
    }
}

I'm still looking into the details of whether or not I need everything that is included because it does increase the time the page takes to load significantly.

War Gravy
  • 1,153
  • 2
  • 14
  • 25