MichaCo / CacheManager

CacheManager is an open source caching abstraction layer for .NET written in C#. It supports various cache providers and implements many advanced features.
http://cachemanager.michaco.net
Apache License 2.0
2.33k stars 457 forks source link

Changes in the object reflect in Cache without updating #359

Closed jkatsiotis closed 2 years ago

jkatsiotis commented 2 years ago

I have the following configuration:

cache = CacheFactory.Build(cacheName, settings => {
    settings
            .WithMaxRetries(3)
            .WithJsonSerializer(jsonSettings, jsonSettings)
            .WithLogging(typeof(CacheLoggerFactory))
            .WithMicrosoftMemoryCacheHandle("inprocess")
        .And
            .WithRedisConfiguration("redis.azure", _redisConnection.Value)
            .WithRedisBackplane("redis.azure")
            .WithRedisCacheHandle("redis.azure", true);

});

When I request an object from the cache and then update a property of the object in C# during an HttpRequest, then, the next time I request this object from the cache the value has been updated.

E.g.

var do = cache.Get("dummyObject");
//do.Text is null
//somewhere in my code I do
do.Text = "Lorem";
//without sending the update to the cache

on an subsequent HttpRequest if I load the object from cache

var do = cache.Get("dummyObject");
do.Text == "Lorem" is true

The project is on ASP.NET Core 5.0. Any thoughts? How can this be happening?

jkatsiotis commented 2 years ago

Here is a sample C# console app demonstrating the issue. Is this expected behavior? If so, any workarounds?

    class Program
    {
        static void Main(string[] args) {
            var manager = CacheFactory.Build("aname", settings => {
                settings.WithMicrosoftMemoryCacheHandle("simplecacheinmemory");
            });

            var dc = manager.Get<DummyClass>("_dummyClass");
            if (dc == null) {
                dc = new DummyClass();
                dc.T = "1";
                manager.AddOrUpdate("_dummyClass", dc, _ => dc);
            }

            RefreshFromCacheAndPrintT(manager);
            dc.T = Guid.NewGuid().ToString();
            RefreshFromCacheAndPrintT(manager);
            dc.T = Guid.NewGuid().ToString();
            RefreshFromCacheAndPrintT(manager);
        }

        static void RefreshFromCacheAndPrintT(ICacheManager<object> manager) {
            var dc = manager.Get<DummyClass>("_dummyClass");
            Console.WriteLine(dc.T);
        }
    }

    public class DummyClass
    {
        public string T { get; set; }
    }
MichaCo commented 2 years ago

InMemory cache does not use serialization to serialize/de-serialize your objects for obvious reasons. And that's just how objects work in memory.

If you cache mutable structures, then, yeah, changes to that instance will be "visible" to all references to that instance.

There is nothing I can do to prevent that. You could prevent it by removing the property setter of your cached classes to make them immutable, or other measures.