ASP.NET MVC - is there an easy way to add data caching to my service level?

I have my MVC application connected so that the repository level queries the LINQ to SQL classes, the service level queries the repository level, and the controllers invoke the service level.

I basically have the code as follows

Repository

Public Function GetRegions() As IQueryable(Of Region) Implements IRegionRepository.GetRegions Dim region = (From r In dc.Regions Select r) Return region.AsQueryable End Function 

Service

  Public Function GetRegionById(ByVal id As Integer) As Region Implements IRegionService.GetRegionById Return _RegionRepository.GetRegions() _ .Where(Function(r) (r.ID = id _ And r.isActive)) _ .FirstOrDefault() End Function Public Function GetRegionByNameAndParentID(ByVal region As String, ByVal parentid As Integer) As Region Implements IRegionService.GetRegionByNameAndParentID Return _RegionRepository.GetRegions() _ .Where(Function(r) (r.Region = region _ And r.ParentID = parentid _ And r.isActive)) _ .FirstOrDefault() End Function Public Function GetActiveRegions() As List(Of Region) Implements IRegionService.GetActiveRegions Return _RegionRepository.GetRegions() _ .Where(Function(r) r.isActive) _ .ToList End Function Public Function GetAllRegions() As List(Of Region) Implements IRegionService.GetAllRegions Return _RegionRepository.GetRegions().ToList End Function 

I am wondering if there is a good / efficient way to add caching to the service level so that it does not always call REPO if the calls are the same.

+4
source share
2 answers

rockinthesixstring - yes, you can add the http cache to this layer using an anonymous function to either pull it out of the repo or extract it from the cache. basically you would do it in the following lines (this is from an application I'm working on now that uses subsonic, but the premise that you are behind it is identical.

  /// <summary> /// Returns an IQueryable based on the passed-in Expression Database /// </summary> IQueryable<T> IRepository<T>.Find(Expression<Func<T, bool>> expression) { // set up our object cacheKey string keyValue = ParseExpression(expression); if(keyValue==null) { return _repository.Find(expression); } string cacheKey = string.Format(EntityrootList, _className, "Find", keyValue, DateTime.UtcNow.Ticks.ToString(), string.Empty); // try to populate from the cache // rockinthesixstring - this is the part that is most relevant to you var result = Cache.Get(cacheKey, () => _repository.Find(expression), CacheDuration); return result; } 

[edit] in the controller you would name it like this (the _repository controller is set as:

 readonly IRepository<Booking> _repository; 

in the example):

 [Authorize] [AcceptVerbs(HttpVerbs.Post)] public ContentResult ListBookings(int shareholderid) { Expression<Func<Booking, bool>> exprTree = x => x.FundShareholderEntity.ShareholderID == shareholderid; var bookings = _repository.Find(exprTree).OrderByDescending(x => x.BookingDetailEntity.ActualDateFrom).OrderBy(x => x.BookingTypeID); return Content(this.RenderPartialToString("BookingListNoPaging", bookings)); } 

In the above example, Cache (i.e. Cache.Get ()) is a class that is more convenient for the httpcontext cache.

hope this helps ...

jim

[edit] - added cache interface to add to the "discussion" :)

 public interface ISessionCache { T Get<T>(string key); T Get<T>(string key, Func<T> getUncachedItem, int cacheDuration); void Insert(string key, object obj, int cacheDuration, CacheDependency arg0, TimeSpan arg2); void Remove(string key); object this[string key] { get; } // default indexer IDictionaryEnumerator GetEnumerator(); } 

in the injection class will be used line by line:

 public class FakeCache : ISessionCache {... all inteface members implemented here etc..} 

or for httpcache:

 public class HttpContextCache : ISessionCache {... all inteface members implemented here etc..} 

etc. etc. welcome again - jim

+1
source

Since caching is a cross-access issue (do a Wikipedia search), you can use policy injection to implement caching at your repository level, but the limitation is that you use a DI infrastructure such as Castle, Unity, .. The advantage of this concept is that you keep clean code at your repository level.

I will start with It Depends, but in a simple scenario where interaction with other service agents is not required, it is only recommended to cache database access, as database access is the slowest of all. Therefore, I would recommend not caching access to the service level, but rather a repository layer. This is also what Martin Fowler describes in his data card template.

If you are in a distributed scenario, according to which your controller and service work on different servers, you can also cache on your controller to prevent serialization of the reference data each time you download, for example. drop-down list or tax code value.

In your scenario, I add a CachingHandler to your GetRegions () repository and create a CacheKey that merges, for example. method and parameters (if any). In a simplified approach, save the CacheKey and the list of results in a Hashtable (in real life, use the Caching application block) and each request to your repository, see if there is a cache key in your Hashtable, then return the cache list.

A quick google search gives you this to get you started: http://entlib.codeplex.com/Thread/View.aspx?ThreadId=34190

+1
source

All Articles