Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8801655
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 14, 20262026-06-14T00:53:24+00:00 2026-06-14T00:53:24+00:00

I am working on a caching manager for a MVC web application. For this

  • 0

I am working on a caching manager for a MVC web application. For this app, I have some very large objects that are costly to build. During the application lifetime, I may need to create several of these objects, based upon user requests. When built, the user will be working with the data in the objects, resulting in many read actions. On occasion, I will need to update some minor data points in the cached object (create & replace would take too much time).

Below is a cache manager class that I have created to help me in this. Beyond basic thread safety, my goals were to:

  1. Allow multiple reads against a object, but lock all reads to that object upon an
    update request
  2. Ensure that the object is only ever created 1 time if
    it does not already exist (keep in mind that its a long build
    action).
  3. Allow the cache to store many objects, and maintain a lock
    per object (rather than one lock for all objects).

    public class CacheManager 
    {
        private static readonly ObjectCache Cache = MemoryCache.Default;
        private static readonly ConcurrentDictionary<string, ReaderWriterLockSlim>
             Locks = new ConcurrentDictionary<string, ReaderWriterLockSlim>();
        private const int CacheLengthInHours = 1;
    
        public object AddOrGetExisting(string key, Func<object> factoryMethod)
        {
            Locks.GetOrAdd(key, new ReaderWriterLockSlim());
    
            var policy = new CacheItemPolicy 
              { 
                 AbsoluteExpiration = DateTimeOffset.Now.AddHours(CacheLengthInHours)
              };
            return Cache.AddOrGetExisting
                (key, new Lazy<object>(factoryMethod), policy);
        }
    
        public object Get(string key)
        {
            var targetLock = AcquireLockObject(key);
            if (targetLock != null)
            {
                targetLock.EnterReadLock();
    
                try
                {
                    var cacheItem = Cache.GetCacheItem(key);
                    if(cacheItem!= null)
                        return cacheItem.Value;
                }
                finally 
                {
                    targetLock.ExitReadLock();
                }
            }
    
            return null;
        }
    
        public void Update<T>(string key, Func<T, object> updateMethod)
        {
            var targetLock = AcquireLockObject(key);
            var targetItem = (Lazy<object>) Get(key);
    
            if (targetLock == null || key == null) return;
            targetLock.EnterWriteLock();
    
            try
            {
                updateMethod((T)targetItem.Value);
            }
            finally
            {
                targetLock.ExitWriteLock();
            }
        }
    
        private ReaderWriterLockSlim AcquireLockObject(string key)
        {
            return Locks.ContainsKey(key) ? Locks[key] : null;
        }
    }
    

Am I accomplishing my goals while remaining thread safe? Do you all see a better way to achieve my goals?

Thanks!

UPDATE: So the bottom line here was that I was really trying to do too much in 1 area. For some reason, I was convinced that managing the Get / Update operations in the same class that managed the cache was a good idea. After looking at Groo’s solution & rethinking the issue, I was able to do a good amount of refactoring which removed this issue I was facing.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-14T00:53:26+00:00Added an answer on June 14, 2026 at 12:53 am

    Well, I don’t think this class does what you need.

    Allow multiple reads against the object, but lock all reads upon an update request

    You may lock all reads to the cache manager, but you are not locking reads (nor updates) to the actual cached instance.

    Ensure that the object is only ever created 1 time if it does not already exist (keep in mind that its a long build action).

    I don’t think you ensured that. You are not locking anything while adding the object to the dictionary (and, furthermore, you are adding a lazy constructor, so you don’t even know when the object is going to be instantiated).

    Edit: This part holds, the only thing I would change is to make Get return a Lazy<object>. While writing my program, I forgot to cast it and calling ToString on the return value returned `”Value not created”.

    Allow the cache to store many objects, and maintain a lock per object (rather than one lock for all objects).

    That’s the same as point 1: you are locking the dictionary, not the access to the object. And your update delegate has a strange signature (it accepts a typed generic parameter, and returns an object which is never used). This means you are really modifying the object’s properties, and these changes are immediately visible to any part of your program holding a reference to that object.

    How to resolve this

    If your object is mutable (and I presume it is), there is no way to ensure transactional consistency unless each of your properties also acquires a lock on each read access. A way to simplify this is to make it immutable (that why these are so popular for multithreading).

    Alternatively, you may consider breaking this large object into smaller pieces and caching each piece separately, making them immutable if needed.

    [Edit] Added a race condition example:

    class Program
    {
        static void Main(string[] args)
        {
            CacheManager cache = new CacheManager();
            cache.AddOrGetExisting("item", () => new Test());
    
            // let one thread modify the item
            ThreadPool.QueueUserWorkItem(s =>
            {
                Thread.Sleep(250);
                cache.Update<Test>("item", i =>
                {
                    i.First = "CHANGED";
                    Thread.Sleep(500);
                    i.Second = "CHANGED";
    
                    return i;
                });
            });
    
            // let one thread just read the item and print it
            ThreadPool.QueueUserWorkItem(s =>
            {
                var item = ((Lazy<object>)cache.Get("item")).Value;
                Log(item.ToString());
                Thread.Sleep(500);
                Log(item.ToString());
            });
    
            Console.Read();
        }
    
        class Test
        {
            private string _first = "Initial value";
            public string First
            {
                get { return _first; }
                set { _first = value; Log("First", value); }
            }
    
            private string _second = "Initial value";
            public string Second
            {
                get { return _second; }
                set { _second = value; Log("Second", value); }
            }
    
            public override string ToString()
            {
                return string.Format("--> PRINTING: First: [{0}], Second: [{1}]", First, Second);
            }
        }
    
        private static void Log(string message)
        {
            Console.WriteLine("Thread {0}: {1}", Thread.CurrentThread.ManagedThreadId, message);
        }
    
        private static void Log(string property, string value)
        {
            Console.WriteLine("Thread {0}: {1} property was changed to [{2}]", Thread.CurrentThread.ManagedThreadId, property, value);
        }
    }
    

    Something like this should happen:

    t = 0ms  : thread A gets the item and prints the initial value
    t = 250ms: thread B modifies the first property
    t = 500ms: thread A prints the INCONSISTENT value (only the first prop. changed)
    t = 750ms: thread B modifies the second property
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I'm working on a web app with very aggressive caching. Virtually every component of
I'm working on a problem that requires caching paginated search results: Paginating very large
I'm working on some JQuery code and I have a question about caching selectors.
I have a web service that is running on a cluster of servers. This
I am working on a caching strategy for our application, which uses both Redis
I'm working on my first RestKit app, and I use CoreData for local caching.
The scenario I am facing is that I have an ASP.NET web service (.NET
I'm trying to get my caching working correctly so that I don't need to
I'm working with an image caching system that generates different images based on a
I am having trouble with caching on a website that I'm working on updating.

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.