Timer in application_start to switch 'current deal'

Been trying to figure out the best method to accomplish this task and I will attempt to explain what I'm thinking as straight forward as possible.

I am working with a website that is similar to woot.com, whiskeymilitia.com etc where there is a deal of the day or of a certain time limit, IE: 15 minute deal. I am trying to create a timer that will run upon application start every x seconds like say every 5 seconds and check whether the deal time limit has elapsed.

I'm currently querying the database to give me the active deal that should be displaying but I'm not entirely sure how I would implement this inside of Application_Start. Basically it would check the deal and the deal limit time + elapsed time and determine whether or not to flag that deal as inactive at that point. Is it possible to route a model I query in application start to a controller?

Any kind of advice would be excellent as this is a new concept to me here and would love to come up with a solid solution!

Feel free to have me clarify anything if needed! Thanks again :)

-- EDIT --

After looking into SqlDependency I ran into an error upon testing everything out.

The SQL Server Service Broker for the current database is not enabled, and as a result query notifications are not supported. Please enable the Service Broker for this database if you wish to use notifications. Your host has to enable SQL Service Broker on your database. Most shared environment hosting companies seem to avoid doing this, stating that it's meant more for a dedicated server environment.

Searching for an alternative. Figured I would drop this off for people to peak at before running into the same thing.

-- EDIT 8:30PM --

So I have tried to utilize the Cache class to accomplis this issue but I'm just completely stuck.

    public ActionResult Index()
    {            

        CurrentDealModel model = new CurrentDealModel();

        model.DealQueueDetails = db.FirstOrDefault<ProductQueue>("SELECT * FROM ProductQueue WHERE Active = 1");

        model.ProductDetails = db.FirstOrDefault<Product>("WHERE Id = @0", model.DealQueueDetails.ProductId);

        return View(model);
    }

I would like to cache this data in the controller for the amount of time stored in the variable model.DealQueueDetails.DealTimeLimit - The problem with this solution is that the administrator will determine how long a deal will display for which means I need to cache based on that value. If there is anyone that could lend some extra help I would be super grateful!

Per James Johnson and others I have tried

Cache.Insert("AbsoluteCacheKey", cacheData, null, DateTime.Now.AddMinutes(15), System.Web.Caching.Cache.NoSlidingExpiration

However setting cacheData to my model is returning a NullReferenceException. Sigh why must I struggle so much on this hehe.

Answers


ASP.NET is not the right place to implement any kind of timing functionality. Use either a scheduled task or a service, or store an expiration date in one of the server-side storage mediums (Cache, Application, etc.) and check it on every request.

I would suggest Cache with a SqlDependency: SqlDependency in an ASP.NET Application

EDIT

If using a SqlDependency is off the table, just set an absolute expiration:

Cache.Insert("AbsoluteCacheKey", cacheData, null, 
    DateTime.Now.AddMinutes(15), System.Web.Caching.Cache.NoSlidingExpiration);

I recommend you to keep the current deal in a Cache, so that you don't have to hit the database. But, then, how will it be updated when necesasry? Well, simple answer: set the expiration of the cache at the absolute time when the deal finishes, so that it has to go to the databse to find the new deal.

Pseudocode:

  public Deal GetDeal()
  {
      if (Cache contains deal)
          return deal from cache;
      else
      {
          get deal
          add deal to cache with absolute expiration
      }   
  }

To use the cache, look at this:

With this implementation you don't have to worry about performance, polling, server restarts, dependencies, or whatever else.


Alright so after 12 hours of tinkering and researching I was finally able to get this implemented correctly and it is working so well It makes me want to start worrying for it lol.

    public ActionResult Index()
    {

        ViewBag.Message = "Welcome to ASP.NET MVC!";

        CurrentDealModel model = new CurrentDealModel();

        model.DealQueueDetails = db.FirstOrDefault<ProductQueue>("SELECT * FROM ProductQueue WHERE Active = 1");

        model.ProductDetails = db.FirstOrDefault<Product>("WHERE Id = @0", model.DealQueueDetails.ProductId);
        var DealTime = model.DealQueueDetails.DealTimeLimit;
        var CurrentTime = System.DateTime.Now;

        TimeSpan span = DealTime.Subtract(CurrentTime);

        var model1 = HttpContext.Cache.Get(model.DealQueueDetails.Id.ToString());
        if (model1 == null)
        {
            model1 = HttpContext.Cache.Add(model.DealQueueDetails.Id.ToString(), model, null, System.DateTime.Now.Add(span).AddSeconds(10), Cache.NoSlidingExpiration, CacheItemPriority.High, new CacheItemRemovedCallback(RedirectToNewDeal));
            var CachedObject = HttpContext.Cache.Get(model.DealQueueDetails.Id.ToString());
            return View(CachedObject);
        }
        return View(model1);

    }

Minus some variables that need to be renamed and some re factoring on this it is working perfectly. I then have a CacheItemRemovedCallback set to the below function.

    public static void RedirectToNewDeal(String key, Object item, CacheItemRemovedReason reason) 
    {
        DatabaseEntitiesDB db = DatabaseEntitiesDB.GetInstance();

        if(reason.ToString() == "Expired")
        {
            var QueueToDisable = db.FirstOrDefault<ProductQueue>("WHERE Id = @0", key);
            QueueToDisable.Active = false;
            QueueToDisable.Update();
        }
    }

Also some refactoring needing to be done to this as well for some error handling/logging however this has worked out just as I had expected. Now the only thing other than this is that I'm having to invoke a AJAX Polling method inside the main few every 3 seconds to check the cache and I'm doing that like so.

     public ActionResult CacheCheck(string key)
    {
        var GetCachedObject = HttpContext.Cache.Get(key);

        if (GetCachedObject != null)
        {
            return Content("NotExpired");
        }
        return Content("Expired");

    }

AJAX Call

setTimeout(function () {
        setInterval(CacheCheck, 2000);
        CacheCheck();
    }, 10000);

function CacheCheck() {
        $.ajax({
            type: "POST",
            url: '@Url.Action("cachecheck", "home", new { key = Model.DealQueueDetails.Id })',
            success: function (data) {                    
                if (data == "Expired") {
                    window.location.reload();
                } 
            }
        })
    }

If anyone knows of a better way to refresh the page and pull in the new deal I am all ears!

Thanks for everyone and their help and I hope this helps other people as well!


Need Your Help

Strategy for links in emails which alter state

asp.net-mvc-3 rest html-email

We've got a few emails that get sent out by our ASP.NET MVC 3 application.

Simple Popularity Algorithm

algorithm popularity

There are plenty of suggested algorithms for calculating popularity based on an item's age and the number of votes, clicks, or purchases an item receives. However, the more robust methods I've seen...