We're using a Linq-to-SQL DataContext in a web application that provides read-only data to the application and is never updated (have set ObjectTrackingEnabled = false to enforce this.
Since the data never changes (except for occasional config updates) it seems wasteful to be reloading it from SQL Server with a new DataContext for each web request.
We tried caching the DataContext in the Application object for all requests to use, but it was generating a lot of error and our research since shows that this was a bad idea, DataContext should be disposed of within the same unit of work, not thread safe, etc, etc.
So since the DataContext is meant to be a data access mechanism, not a data store, we need to be looking at caching the data that we get from it, not the context itself.
Would prefer to do this with the entities and collections themselves so the code can be agnostic about whether it is dealing with cached or "fresh" data.
How can this be done safely?
First, I need to make sure that the entities and collections are fully loaded before I dispose of the DataContext. Is there a way to force a full load of everything from the database conveniently?
Second, I'm pretty sure that storing references to the entities and collections is a bad idea, because it will either
(a) cause the entities to be corrupted when the DataContext goes out of scope or
(b) prevent the DataContext from going out of scope
So should I clone the EntitySets and store them? If so, how? Or what's the go here?
This is not exactly an answer to your question, but I suggest avoiding caching on web site side.
I would rather focus on optimizing database queries for faster and more efficient data retrieval.
Caching will:
- not be scalable
- need extra code for synchronization, I assume your data isn't completely static in DB?
- extra code will be bug prone
- will eat up memory of your web server quickly, the next thing you might end up addressing is memory issue on your web server
- will not work very well, when you need to load-balance your web site
[Edit]
If I needed to cache 5MB data, I would use Cache object, probably with lazy loading. I would use a set of lightweight collections, like ReadonlyCollection<T>
, Collectino<T>
. I would probably use ReadonlyDictionary<TKey, TValue>
also for quick searches in the memory. I would use LINQ-to-Objects to manipulate with the collections.
You want to cache the data retrieved from the DataContext rather than the DataContext object itself. I usually refactor out commonly-retrieved data into methods that I can implement silent caching with, something like this (may need to add thread-safe logic):
public class MyBusinssLayer {
private List<MyType> _myTypeCache = null;
public static List<MyType> GetMyTypeList() {
if (_myTypeCache == null) {
_myTypeCache = // data retrieved from SQL server
}
return _myTypeCache
}
}
This is the simplest pattern that can be used and will cache for one web request. To cache for longer periods, store the contents in a longer-term storage, such as Application
or Cache
. For instance, to store in Application
level data, use this kind of pattern.
public static List<MyType> GetMyTypeList() {
if (Application["MyTypeCacheName"] = null) {
Application["MyTypeCacheName"] = // data retrieved from SQL server
}
return (List<MyType>)Application["MyTypeCacheName"];
}
This would be for data that almost never changes, such as a static collection of status types to choose from in a DropDownList
. For more volitile data, you can use the Cache
with a timeout period, which should be selected based on how often the data changes. With the Cache
items can be invalidated manually with code if necessary, or with a depedency checker like SqlCacheDependency
.
Hope this helps!