I need to cache a very small amount of data for a maximum of one hour for an ASP.NET web application (one instance). Obviously this needs to be thread-safe so I can access the cache from within my requests.
I want to do this "in process", and not use anything external.
What would be the easiest way to implement this?
You can user the Cache object ASP.NET provides you with.
You can create a property that returns the cached object if exist else retrieve it from db.
private myClass myProp {
get{
if (Cache["Key1"] == null)
Cache.Add("Key1", "Value 1", null, DateTime.Now.AddMinutes(60), Cache.NoSlidingExpiration, CacheItemPriority.High);
return (myClass)Cache["Key1"];
}
}
Use static variables. You could write a static cache class including your update logic (maximum of one hour) and store the retrieved data in a static member.
The class will persist in the app pool until it is recycled. This could be too often or too rarely for your use cases. But the caching ability should be fair enough.
For the thread-safety issues you could provide getter methods in this class and make use of the lock statement.
Related
Some background:
Working with:
.NET 4.5 (thinking of migrating to 4.5.1 if it's painless)
Web Forms
Entity Framework 5, Lazy Loading enabled
Context Per Request
IIS 8
Windows 2012 Datacenter
Point of concern: Memory Usage
Over the project we are currently on, and probably our first bigger project, we're often reading some bigger chunks of data, coming from CSV imports, that are likely to stay the same for very long periods of time.
Unless someone explicitly re-imports the CSV data, they are guaranteed to be the same, this happens in more than one places in our project and similar approach is used for some regular documents that are often being read by the users. We've decided to cache this data in the HttpRuntime cache.
It goes like this, and we pull about 15,000 records consisting mostly of strings.
//myObject and related methods are placeholders
public static List<myObject> GetMyCachedObjects()
{
if (CacheManager.Exists(KeyConstants.keyConstantForMyObject))
{
return CacheManager.Get(KeyConstants.keyConstantForMyObject) as List<myObject>;
}
else
{
List<myObject> myObjectList = framework.objectProvider.GetMyObjects();
CacheManager.Add(KeyConstants.keyConstantForMyObject, myObjectList, true, 5000);
return myObjectList;
}
}
The data retrieving for the above method is very simple and looks like this:
public List<myObject> GetMyObjects()
{
return context.myObjectsTable.AsNoTracking().ToList();
}
There are probably things to be said about the code structure, but that's not my concern at the moment.
I began profiling our project as soon as I saw high memory usage and found many parts where our code could be optimized. I never faced 300 simultaneous users before and our internal tests, done by ourselves were not enough to show the memory issues. I've highlighted and fixed numerous memory leaks but I'd like to understand some Entity Framework related unknowns.
Given the above example, and using ANTS Profiler, I've noticed that 'myObject', and other similar objects, are referencing many System.Data.Entity.DynamicProxies.myObject, additionally there are lots of EntityKeys which hold on to integers. They aren't taking much but their count is relatively high.
For instance 124 instances of 'myObject' are referencing nearly 300 System.Data.Entity.DynamicProxies.
Usually it looks like this, whatever the object is:
Some cache entry, some object I've cached and I now noticed many of them have been detached from dbContext prior caching, the dynamic proxies and the objectContext. I've no idea how to untie them.
My progress:
I did some research and found out that I might be caching something Entity Framework related together with those objects. I've pulled them with NoTracking but there are still those DynamicProxies in the memory which probably hold on to other things as well.
Important: I've observed some live instances of ObjectContext (74), slowly growing, but no instances of my unitOfWork which is holding the dbContext. Those seem to be disposed properly per request basis.
I know how to detach, attach or modify state of an entry from my dbContext, which is wrapped in a unitOfWork, and I often do it. However that doesn't seem to be enough or I am asking for the impossible.
Questions:
Basically, what am I doing wrong with my caching approach when it comes to Entity Framework?
Is the growing number of Object Contexts in the memory a concern, I know the cache will eventually expire but I'm worried of open connections or anything else this context might be holding.
Should I be detaching everything from the context before inserting it into the cache?
If yes, what is the best approach. Especially with List I cannot think of anything else but iterating over the collection and call detach one by one.
Bonus question: About 40% of the consumed memory is free (unallocated), I've no idea why .NET is reserving so much free memory in advance.
You can try using non entity class with specific properties with SELECT method.
public class MyObject2 {
public int ID { get; set; }
public string Name { get; set; }
}
public List<MyObject2> GetObjects(){
return framework.provider.GetObjects().Select(
x=> new MyObject2{
ID = x.ID ,
Name = x.Name
}).ToList();
);
}
Since you will be storing plain c# objects, you will not have to worry about dynamic proxies. You will not have to call detach on anything at all. Also you can store only few properties.
Even if you disable tracking, You will see dynamic proxy because EF uses dynamic class derived from your class which stores extra meta data information (relation e .g. name of foreign key etc to other entities) for the entity.
steps to reduce memory here:
Re new the context, often
Dont try and delete content from the Context. Or Set it to detached.
It hangs around like a fart in a phone box
eg context = new MyContext.
But if possible you should be
using (var context = new Mycontext){ .... }
//short lived contexts is best practice
With your Context you can set Configurations
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false; //<<<<<<<<<<< THIS one
this.Configuration.AutoDetectChangesEnabled = false;
you can disable proxies if you still feel they are hogging memory.
But that may be unecesseary if you apply using to the context in the first place.
I would redesign the solution a bit:
You are storing all data as a single entry in cache
I would move this and have an entry per cache item.
You are using HTTPRuntime cache
I would use Appfabric Caching, also MS, also free.
Not sure where you are calling that code from
I would Call it on Application start, then all data is in memory when the user needs it
You are using Entity SQL
For this I would use an Entity Data Reader http://msdn.microsoft.com/en-us/library/system.data.entityclient.entitydatareader(v=vs.110).aspx
See also:
http://msdn.microsoft.com/en-us/data/hh949853.aspx
I just discovered System.Web.Caching.Cache used in a project that I am working on and I am having a hard time finding more information on it.
My question is how this cache is persisted? Is it client-side (similar to ViewState), server-side (Session)? Entirely different?
Example:
protected string FileContent
{
get
{
return Cache[FILE_UPLOAD_KEY + Id] as string ?? GetFileUpload();
}
}
It's a server-side, application-wide cache.
One instance of this class is created per application domain, and it
remains valid as long as the application domain remains active.
Information about an instance of this class is available through the
Cache property of the HttpContext object or the Cache property of the
Page object. (Cache Class, MSDN)
It grants the ability to set time limits and so forth on cached objects. And it doesn't promise the object will be there when you need it again. It keeps items in cache only so long as there is sufficient memory to do so.
So, it's not intended for passing objects between page views (use ViewState or Session for that) or controls (use Items for that). It's intended to cache global objects (accessible in any request from all clients) that are expensive to build.
It's persisted at the server, and it's global across sessions, like Application. So when you set a value in the Cache, it's available to all users until it expires.
EDIT
The example you've got probably isn't quite right (unless GetFileUpload() actually writes to the cache). Generally your calls to cache look something like:
string GetSomeStringFromCache()
{
string someString = Cache[SomeKey] as string;
if (someString == null)
{
someString = GetStringUsingSomeExpensiveFunction();
Cache.Add(SomeKey, someString, /*a bunch of other parameters*/);
}
return someString;
}
This will put it in the Cache if it's not already there, but if it is, it will just use it.
Say I have a page in my web application that lets a user update their contact information. Pretend in order to retrieve or save this information I have the following class:
public class User
{
DataAccesClass dataAccesClass = new DataAccesClass()
public string UserName {get;set;}
public string Address {get;set;}
public string EmailAddress {get;set;}
public User(){}
public static User GetUser(int userID)
{
User user = dataAccesClass.GetUser(userID); //
return user;
}
public void Save()
{
dataAccesClass.SaveUser(this);
}
}
Say that on my Page_Load event I create a new instance of my User class (wrapped in a !isPostBack). I then use it's public properties to populate text fields on said page in my web application. Now the question is... When the page is posted back, what is the correct way to rebuild this class to then save the updated information? Because the class was created on Page_Load !isPostBack event it is not available. What is the correct way to handle this? Should I store it in a Session? ViewState? Should I simply rebuild it every post back? The User class in this example is small so that might influence the correct way to do it but I'd like to be able to take the same approach for much larger and more complex classes. Also, would this class be considered an acceptable business object?
what is the correct way to rebuild this class to then save the updated information?
I would say the best practice would be do not rebuild the class on every postback. You should build the data on the first request, set values on controls, then let the viewstate on those controls persist the data.
If there is a potential for the data to need to be updated, tie re-generation of the object to an event indicating there is actual need to update.
Should I store it in a Session? ViewState? Should I simply rebuild it every post back?
Selecting whether to store the value in session or re-pull from the data layer should be based on the memory footprint of the object, the scalability requirements of the application, the costliness of the database operation, and the likelihood that the object will need to be accessed on any particular request. So I believe that is highly situational.
Also, would this class be considered an acceptable business object?
I don't have a lot of experience with BLL's but it looks like you're on the right track.
Unless profiling indicates otherwise, it's okay to just reconstruct the object with every request. You can also implement some kind of caching in your data access code. Your class is an acceptable business object.
Given that User object might have info you wouldn't want to expose through ViewState, it can be stored in Session.
This is the "standard" way of doing this in ASP.NET.
In the case of your example, reconstructing the object looks fine as it is small. But if you have a small object you inevitably store for a while, I would use session. If the object is large, I would directly use database or session with database connection.
Depending how complex you are thinking of getting a JavaScript framework called knockout.js might be a good fit. You could create a json object to bind to a jQuery template that would build the HTML depending on the object, it handles complex objects very well.
My requirement is to call WCF web service from ASP.NET code behind and pass some data for example:
void Add(int x, int y);
Result of the operation should be somehow stored within WCF web service (member variable).
Later user should be able to call
double Equals();
and get the result of the operation. Of course since it's an open website many non-authenticated users can open the ASP.NET page and call the web service.
Alternatively Add method can return "key" that can be used to get result back, for example :
Guid Add(int x, int y);
double Equals(Guid key);
I don't think I can use WCF sessions since web service will think that there is only 1 client ( WebSite calling the webserivce hosted in IIS ).
Users are not authenticated - they are anonymous users.
I could use : ( please take a look at my EDIT )
InstanceContextMode =InstanceContextMode.Single
as attribute over my web service and store Dictionary<key, result> but the problem is when can I remove entry from Dictionary? Should I use some sort of BackgroundWorker to check dictionary every e.g. 15 minutes and remove old entries? In that case I would have to store DateTime against each entry but that's not a problem if this is the best way to implement this solution...
My example Add/Equals methods are only to illustrate the problem, real requirement need to store much more data per user than simple double result
Thanks in advance for all the help.
EDIT:
After thinking more about the InstanceContextMode =InstanceContextMode.Single I think it's not the best option to use with ASP.NET since if 100 users will call the web service at the same time I will get 100 sequential invocations which will cause performance issues.
In that case do you have any ideas how to implement this without changing InstanceContextMode setting (which by default is PerSession as far as I know) ?
I think,Keeping the
InstanceContextMode =InstanceContextMode.Single
as the service behavior, if your architecture permits, you can use the System.Web.Caching.Cache object implemented in a static class(so that it will be instantiated only once) ,where you can specify the cache dependency/expiration time etc and hence you can get the complete set of functionalities offered from the cache service, instead of implementing on your own.
Edit:
If your primary intention is about Caching, then you need not use the Instance Context mode even. You can simply implement your own static Cache provider based on the System.Web.Caching.Cache and pass the cacheKey as shown in the following sample:
public int Add(int a, int b)
{
string cacheKey = a.ToString()+b.ToString();
if (!CacheProvider.Contains(cacheKey))
{
CacheProvider.Add(cacheKey, a + b);
return a + b;
}
else
{
return (int)CacheProvider.GetValue(cacheKey);
}
}
CacheProvider in the above code is the static implementation of your Cache provider.
I have created a Registry class in .NET which is a singleton. Apparently this singleton behaves as if it were kept in the Cache (the singleton object is available to each session). Is this a good practice of should I add this Singleton to the Cache?
+ do I need to wacth out for concurrency problems with the GetInstance() function?
namespace Edu3.Business.Registry
{
public class ExamDTORegistry
{
private static ExamDTORegistry instance;
private Dictionary<int, ExamDTO> examDTODictionary;
private ExamDTORegistry()
{
examDTODictionary = new Dictionary<int, ExamDTO>();
}
public static ExamDTORegistry GetInstance()
{
if (instance == null)
{
instance = new ExamDTORegistry();
}
return instance;
}
}
}
Well, your GetInstance method certainly isn't thread-safe - if two threads call it at the same time, they may well end up with two different instances. I have a page on implementing the singleton pattern, if that helps.
Does your code rely on it being a singleton? Bear in mind that if the AppDomain is reloaded, you'll get a new instance anyway.
I don't really see there being much benefit in putting the object in the cache though. Is there anything you're thinking of in particular?
Despite their presence in GoF singletons are generally considered bad practice. Is there any reason why you wish to have only one instance?
HttpContext.Cache is available to all sessions, but items in the cache can be removed from memory when they expire or if there is memory pressure.
HttpContext.Application is also available to all sessions and is a nice place to store persistent, application-wide objects.
Since you've already created a singleton and it works, I don't see why should use one of the ones built-in singleton collections instead, unless you need the extra functionality that Cache gives you.
Not sure sure what you mean by cache... if you want this cached (as in... keep it in memory so that you don't have to fetch it again from some data store) then yes, you can put it in the cache and it will be global for all users. Session means per user, so I don't think this is what you want.
I think the original question spoke to which was preferred. If you have data that remains static or essentially immutable, then http caching or singleton pattern makes a lot of sense. If the singleton is loaded on application start up then there is no "Threading" issue at all. Once the singleton is in place you will receive the same Instance you requested. The problem with a lot of what I am seeing in actual implementations is that people are using both without fully thinking it out. Why should you expire immutable configuration data? Had one client that cached there data and still created ADO DB objects etc. when last they checked if it was in cache. Effectively both of these solutions will work for you, but to gain any positive effect, make sure you use the cache/singleton. In either case, if your data is not available, both should be refreshed at that moment.
i would make it like:
private static READONLY ExamDTORegistry instance;
then you dont need to check for NULL and its thread safe.