I have an ASP.NET MVC 3 app that is basically just a set of web services. These web services are exposed by a set of Controller actions. Each controller action queries my database. Because my data rarely changes, and, stale data is not a concern, I thought i would implement some cacheing to improve performance. My goals are:
Never cache a response to a user.
Cache the database records for up to 24 hours. If 24 hours has passed, hit the database again.
Does that make sense? I know how to prevent the response from caching. I just use the following:
HttpContext.Response.Cache.SetCacheability(cacheability)
However, I'm not sure how to cache my database records in memory for up to 24 hours. Does anyone have any suggestions on how to do this? I'm not even sure where to look.
Thank you
You can use the System.Runtime.Caching namespace (or the ASP.NET cache, but this is older and can only be used within web applications).
Here's a sample function which you can use to wrap around your current data retrieval mechanism. You can alter the parameters in MemoryCache.Add to control how much it's cached, but you requested 24h above.
using System.Runtime.Caching; // At top of file
public IEnumerable<MyDataObject> GetData()
{
IEnumerable<MyDataObject> data = MemoryCache.Default.Get(MYCACHEKEY) as IEnumerable<MyDataObject>;
if (data == null)
{
data = // actually get your data from the database here
MemoryCache.Default.Add(MYCACHEKEY, data, DateTimeOffset.Now.AddHours(24));
}
return data;
}
As mentioned by #Bond, you may also wish to look at using an SQL Cache Dependency if the data you're caching is likely to change within the 24 hours. If it's pretty static though, this will do what you asked.
The MVC framework is persistence-agnostic. There are no built-in means to store data, so there are no built-in means to cache stored data.
The OutputCache attribute can be used to cache a server response. But you explicitly stated that that's not something you want to do.
However, you may still be able to use the built-in OutputCache if you want to stay within the MVC framework. Consider exposing the data you want to cache as a JSON result
[OutputCache(Duration = 86400)]
public JsonResult GetMyData() {
var results = QueryResults();
return Json(results);
}
The JSON string at /ControllerName/GetMyData will be cached for 24 hours, so the actual query will only be ran once per day. That means you'd have to implement an AJAX call on your final page, or make another HTTP call from your server. Neither of those are ideal.
I would look for another solution to your problem outside of the MVC framework. Consider memcached, which was created for exactly this purpose.
What you are talking about is not exactly MVC responsibility. ASP.Net allow you to cahce only the things it produces (and this they are responces, obviosly).
If you want to cache data it can be better to cachet it the same place it was produced - somewhere in BL or Data Layer.
You can do something like that:
public class DataCacher
{
private static String data;
private static DateTime updateTime;
private DataCacher() { }
public static String Data
{
get
{
if (data == null || updateTime > DateTime.Now)
{
data = "Insert method that requests your data form DB here: GetData()";
updateTime = DateTime.Now.AddDays(1);
}
return data;
}
}
}
String data presents your actual data here. After adding this class replace your GetData() methods with DataCacher.Data.
Hope it helps or at least leads you to some further thinking.
If you're using MSSQL you may want to look at SQL Cache Dependency.
I'm not sure if you can configure cache expiration to 24 hours but with the cache dependency you may not need to - it will invalidate the cache as soon as there is an update on the database(i.e. should be more efficient than the time expiration strategy).
Here is a good article which discusses several performance related practices for ASP.NET MVC 3 and caching is mentioned.
Related
Wanted to know about - What is the different between HttpContext.Response.Cache and HttpContext.Current.Cache objects ? and What should be used in Asp.net MVC web application?
Why I am asking this question?
Because, I have my own [NoCache] attribute, which is responsible to avoid cache during the view redirection.
E.g.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var cache = filterContext.HttpContext.Response.Cache;
cache.SetExpires(DateTime.Now.AddDays(-1));
cache.SetValidUntilExpires(false);
cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
cache.SetCacheability(HttpCacheability.NoCache);
cache.SetNoStore();
base.OnActionExecuting(filterContext);
}
And I am using this above attribute in my BaseController, like..
[NoCache]
public class BaseController : Controller
{
}
This works fine!
BUT, in the authentication part - I am storing some information in the cache by below mechanism
public ActionResult Login()
{
HttpContext.Current.Cache.Insert("someKey", "someValue", null, expiredTime.Value, Cache.NoSlidingExpiration);
return view();
}
SO, my Question is..
I am using my custom attribute in base class of controller, which is responsible to clear the cache items, even though, I can still access the cache key and value throughout the application which was set by the Login method code..
Why this both cache mechanism act differently? What is the difference between these two?
Could you please suggest some idea or information on this.
HttpContext.Current.Cache is a class that provides caching of any kind of serializable objects. It in itself equates to HttpRuntime.Cache to muddy your waters even more.
We use HttpContext.Current.Cache usually to cache data from our database servers. This saves having to continually ask the database for data that changes little. This is entirely server-side and does not affect the client.
HttpResponse.Cache allows you to set up and control the various cache control headers sent with the response content. This tells the client (and any in-between proxies) what kind of caching you suggest. Note I say suggest, because it's entirely arbitrary whether the client honors it or not.
What is the best method of storing a shared object in asp.net? It will get called multiple times per request on every request. Ive been using these two methods but Id like to know if there is a better way. I refresh this object once an hour.
public static List<ResourceObject> SharedResources = new List<ResourceObject>()
//OR
public static List<ResourceObject> SharedResources
{
get
{
List<ResourceObject> _sharedResources = HttpContext.Current.Cache["RedirectRoutes"] as List<ResourceObject>;
if (_sharedResources == null)
{
_sharedResources = LoadNewSharedResource();
HttpContext.Current.Cache["RedirectRoutes"] = _sharedResources;
}
return _redirectRoutes;
}
set
{
HttpContext.Current.Cache["RedirectRoutes"] = value;
}
}
If your object is changing frequently (i.e. hourly as you mentioned) then you'll be best to use the cache as it will be able to take care of flushing for you (assuming you pass the correct parameters when adding the value to the cache). If you use a static value it will not be cleared out every hour automatically so you'd need to implement the check yourself.
If this is, as it seems, an object that needs to persist across requests, then this is a perfectly good and reasonable way to achieve it. You may want to put the cached version in a local variable if it is being accessed multiple times within one call, to save retrieving it from the cache each time.
Is there a specific issue with caching it like that that you are concerned about?
I am using webmethod which fetches data from database & storing it in a static variable so that next time it will not hit the database becoz the data is being accessed frequently. Creating static variable doesn't seem to be a proper solution what is an alernative to this scenario.
e.g
public static List<SupplierGridData> lstFullSupplierData;
public static List<SupplierGridData> GetProductData()
{
if (lstFullSupplierData == null)
{
//first time get data from database
lstFullSupplierData = new List<SupplierGridData>();
lstFullSupplierData = new POProcess().GetInquiryDetails();
}
else
{
//use lstFullSupplierData which holds data
}
}
What you want is caching:
http://msdn.microsoft.com/en-us/library/xsbfdd8c.aspx
More specifically, caching data:
http://msdn.microsoft.com/en-us/library/6hbbsfk6.aspx
There is also Session State:
http://msdn.microsoft.com/en-us/library/ms972429.aspx
This comes in the per-application and per-user guise. And you can also change the backing store of session state.
The easiest solution to this could be hitting the db once if(!this.IsPostBack) then burying that stuff in the viewstate of the page if it's not too much stuff (assuming you don't need to carry across other pages). Otherwise session or cache will do as other people are suggesting.
You need to store your data in session in order to not hit the database again.
http://msdn.microsoft.com/en-us/library/ms178581.aspx
I want to maintain model state between method calls on the controller.
Is that possible?
A controller is created at the call of some of its methods and destroyed when the called method finishes its execution?
If not... how can I maintain the state between method calls?
If that is possible... how has it to be written?
thanks
Let's keep this simple! :-)
You asked following questions:
I want to maintain model state between method calls on the controller.
Is that possible?
Answer: yes, it is
A controller is created at the call of some of its methods and destroyed when the called method finishes its execution?
Answer: yes, it doesn't persist any state - it just returns the result generated by the action
If not... how can I maintain the state between method calls? If that is possible... how has it to be written?
Answer:
We had the same issue in our current business application using ASP.NET MVC2 and came up with following solutions:
Solution(s):
1st he Proof-Of-Concept for this application contained some special functionality using the global Session. (Note that the session is saved on the webserver)
Saving and reading from session in ASP.NET MVC:
public Action SaveIntoSession()
{
...
Session["SessionData"] = "Something to be stored in session";
...
}
public action ReadFromSession()
{
...
// UNBOXING is required when you're using the session as ASP.NET doesn't know what is stored into the session
string sessionData = (string)Session["SessionData"];
...
}
2nd There's another concept of saving the required information for each request into a Session Table into your database. (But I won't go too deep into that.. check this article for more info - although it's php, you can get your head around the concept pretty easily)
3rd Would be to use the TempDataDictionary as mentioned by Charlino. The problem with this is, that it only persists the state from one call to the another. All data that is stored will be deleted by the next request. (TempData is mostly used to display errors to the user..)
public Action SaveIntoTempData()
{
...
TempData["TempData"] = "Something to be stored ONE request ONLY";
...
}
public Action ReadFromTempData()
{
...
string tempData = (string)TempData["TempData"];
}
4th You could also use the ViewDataDictionary. It is not recommended to be used with sensitive data. (check this thread for more info)
public Action SaveIntoViewData()
{
...
TempData["ViewData"] = "Something to be stored into the page";
...
}
public Action ReadFromViewData()
{
...
string viewData = (string)ViewData["ViewData"];
}
At the end of the day. It is up to you and your team what best matches requirements.
You have access to the standard ASP.NET session, even in MVC. Check the docs.
Would TempData / TempDataDictionary be suitable?
HTHs,
Charles
I think by default you should be trying for a Restfull approach. If that is not viable then either serialise to the session object or store in a Singleton or something like that.
By default I don't think MVC maintains state between calls.
What can I do to avoid the repeated loading of static data every time a page is loaded in ASP.NET MVC2? This is pretty wasteful, and I would like to fix it.
My master page has the typical User Name + Company Name display in the top right corner. But because of the stateless nature of MVC, this data has to be looked up every single time a page is loaded, even though it never changes.
In the old webforms days I would just throw it into Session, but that seems to be discouraged in MVC. Plus the web app runs on a webfarm, so I really do not want to use session.
The web app already has quite a few static data items that get queried on every page load (user currency, user tax rate, etc), so I think a performance gain can be made by loading them only once at login time. I'm just not sure what the correct MVC method is (I am still quite new to MVC).
I always use the built-in ASP.NET cache for this, set the expiry appropriately and you're good to go.
public void Blah()
{
var company = HttpRuntime.Cache.Get("Company") as string;
if (company == null)
{
company = FetchCompanyFromDb();
HttpRuntime.Cache["Company"] = company;
}
return this.View(company);
}
Also see this previous question:
Caching in asp.net-mvc
Could you maybe encapsulate your User currency / User name / Tax etc into a user control and then use Output Caching on that?