Castle Active Record - Working with the cache - c#

im new to the Castle Active Record Pattern and Im trying to get my head around how to effectivley use cache.
So what im trying to do (or want to do) is when calling the GetAll, find out if I have called it before and check the cache, else load it, but I also want to pass a bool paramter that will force the cache to clear and requery the db.
So Im just looking for the final bits.
thanks
public static List<Model.Resource> GetAll(bool forceReload)
{
List<Model.Resource> resources = new List<Model.Resource>();
//Request to force reload
if (forceReload)
{
//need to specify to force a reload (how?)
XmlConfigurationSource source = new XmlConfigurationSource("appconfig.xml");
ActiveRecordStarter.Initialize(source, typeof(Model.Resource));
resources = Model.Resource.FindAll().ToList();
}
else
{
//Check the cache somehow and return the cache?
}
return resources;
}
public static List<Model.Resource> GetAll()
{
return GetAll(false);
}

Take a look at the caching pattern:
http://weblogs.asp.net/ssmith/archive/2003/06/20/9062.aspx
http://stevesmithblog.com/blog/cache-access-pattern-revised/
BTW you're initializing ActiveRecord each time you call GetAll. You have to initialize only once, when your application starts.
Also, it's not good practice generally to explicitly release the cache like that. Instead, use some sort of policy or dependency (see for example SqlDependency)
Also, NHibernate has a pluggable second-level cache.

Related

How to clear MemoryCache in ASP.NET Core?

How to correctly clear IMemoryCache from ASP.NET Core?
I believe this class is missing Clear method, but anyway how to deal with it? In my project I'm caching DocumentRepository's methods for 24 hours where I'm getting lots of rows from database. But sometimes I can change the database, so I want to clear the IMemoryCache as it has got rubbish data.
IMemoryCache indeed lacks a Clear() method, but it's not hard to implement one yourself.
public class CacheWrapper
{
private readonly IMemoryCache _memoryCache;
private CancellationTokenSource _resetCacheToken = new();
public CacheWrapper(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public void Add(object key, object value, MemoryCacheEntryOptions memoryCacheEntryOptions)
{
using var entry = _memoryCache.CreateEntry(key);
entry.SetOptions(memoryCacheEntryOptions);
entry.Value = value;
// add an expiration token that allows us to clear the entire cache with a single method call
entry.AddExpirationToken(new CancellationChangeToken(_resetCacheToken.Token));
}
public void Clear()
{
_resetCacheToken.Cancel(); // this triggers the CancellationChangeToken to expire every item from cache
_resetCacheToken.Dispose(); // dispose the current cancellation token source and create a new one
_resetCacheToken = new CancellationTokenSource();
}
}
Based on https://learn.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-3.1#cache-dependencies and https://stackoverflow.com/a/45543023/2078975
There is an easy way to clear it that may not work in all environments, but where it does work, it works well.
If you're getting your IMemoryCache object through dependency injection, there is a good chance you'll actually be receiving a MemoryCache object. MemoryCache does have a method called Compact() that will clear the cache. You can try to cast the object to MemoryCache and, if successful, call Compact.
Example:
string message;
// _memoryCache is of type IMemoryCache and was set via dependency injection.
var memoryCache = _memoryCache as MemoryCache;
if (memoryCache != null)
{
memoryCache.Compact(1);
message ="Cache cleared";
} else {
message = "Could not cast cache object to MemoryCache. Cache NOT cleared.";
}
The cache class and interface don't have any methods for clearing neither ones to iterate over the keys, since it's not meant to be a list and in ASP.NET Core applications one usually use IDistributedCache interface as dependency, since it easier allows you to later change from a local memory cache to a distributed cache (such as memd or Redis).
Instead, if you want to invalidate a specific row, you should remove the cached entry via cache.Remove(myKey).
Of course, this requires you to know the key you want to invalidate. Typically you do that via events. Every time you update an entry in the database, you would fire up an event. This event will be caught by a background service and cause a cache invalidation.
Locally this can be done with any pub/sub library. In distributed scenarios you may want to use pub/sub features of the distributed cache (i.e. Redis).
In cases of lookup tables (where many values are affected), you can have a service refresh the cache (i.e. every 5 to 10 minutes via a background task using a scheduling library such as hangfire or quart.net).
Home work Questions
But one question you should ask yourself: Is it really a good idea to cache documents for 24 hours if they change frequently?
Does the loading of a single document takes so much time, that caching it 24 hours will be worth? Or are shorter times good enough (15, 30, 60 minutes)?
To clear you MemoryCache, just remove your memory cache by key, like this:
memoryCache.Remove("your_key");

Caching Data in ASP.NET MVC 3

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.

Update part of cache using spring.net in repository possible?

I'm building a repository with caching using spring.net. Can I update/add/delete one item in the cached list without having to rebuild the whole list?
Looking at the documentation and the example project from their site they always clear the cache whenever they update/add/delete one item. Therefore as long as you only read an object or the list of objects the caching works well but it feels stupid having to rebuild the whole cache just because I change one item?
Example:
// Cache per item and a list of items
[CacheResult("DefaultCache", "'AllMovies'", TimeToLive = "2m")]
[CacheResultItems("DefaultCache", "'Movie-' + ID")]
public IEnumerable<Movie> FindAll()
{
return movies.Values;
}
// Update or add an item invalidating the list of objects
[InvalidateCache("DefaultCache", Keys = "'AllMovies'")]
public void Save([CacheParameter("DefaultCache", "'Movie-' + ID")]Movie movie)
{
if (this.movies.ContainsKey(movie.ID))
{
this.movies[movie.ID] = movie;
}
else
{
this.movies.Add(movie.ID, movie);
}
}
Having mutable things stored in the cache seems to me a fountain of horrible side effects. Imho that is what you would need if you want to add/remove entries from a cached list.
The implementation of CacheResultAdvice and InvalidateCacheAdvice allows to store and invalidate an object (key) -> object (value) combination. You could add another layer and retrieve movie per movie but I think that it is just a case of premature optimization (with the opposite effect).
CacheResultAdvice
InvalidateCacheAdvice
Edit:
Btw. if you use a mature ORM look for integrated level2 caching, if you want to avoid hitting the db server: http://www.klopfenstein.net/lorenz.aspx/using-syscache-as-secondary-cache-in-nhibernate

shared asp.net object as static or cache

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?

Application_End() cannot access cache through HttpContext.Current.Cache[key]

I want to be able to maintain certain objects between application restarts.
To do that, I want to write specific cached items out to disk in Global.asax Application_End() function and re-load them back on Application_Start().
I currently have a cache helper class, which uses the following method to return the cached value:
return HttpContext.Current.Cache[key];
Problem: during Application_End(), HttpContext.Current is null since there is no web request (it's an automated cleanup procedure) - therefore, I cannot access .Cache[] to retrieve any of the items to save to disk.
Question: how can I access the cache items during Application_End()?
If you want to get access to cache object before it will be disposed, you need to use somethink like this to add object to cache:
Import namespace System.Web.Caching to your application where you are using adding objects to cache.
//Add callback method to delegate
var onRemove = new CacheItemRemovedCallback(RemovedCallback);
//Insert object to cache
HttpContext.Current.Cache.Insert("YourKey", YourValue, null, DateTime.Now.AddHours(12), Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, onRemove);
And when this object is going to be disposed will be called following method:
private void RemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
//Use your logic here
//After this method cache object will be disposed
}
I strongly urge you to rethink your approach. You may want to describe specifics of what are you trying to do, so we might help you with that.
But if you are totally set on it, then you can simply save values on disk when you actually set them, i.e. your helper class would looks something like this:
public static class CacheHelper
{
public static void SetCache(string key, object value)
{
HttpContext.Current.Cache[key] = value;
if (key == "some special key")
WriteValueOnDisk(value);
}
}
You can access the cache through HttpRuntime.Cache when you don't have an HttpContext available. However, at Application_End, i believe the cache is already flushed.
The solution Dima Shmidt outlines would be the best approach to store your cached values. That is by adding your items to cache with a CacheItemRemovedCallback, and store the values to disk there.
As an alternative solution you could store the data in Application object (Application[key]) or simply create a static class and use it to keep your data within app - in this case the data would sill be available upon Application_End.

Categories