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 458 forks source link

CacheManager core with couchdb throws error while trying to get an exsiting cache item #297

Open abdou89 opened 4 years ago

abdou89 commented 4 years ago

Hello, I'm using Cachemanager core (v 1.2.0 ) with the following config


var definition = new CouchbaseClientDefinition();
            configuration.GetSection("CoucheDb").Bind(definition);
            var clientConfig = new ClientConfiguration(definition);
            ICacheManager<string> CacheInstance = CacheFactory.Build<string>(settings =>
            {
                settings
                 //Memory cach 
                 //.WithMicrosoftMemoryCacheHandle(). WithExpiration(ExpirationMode.Sliding, TimeSpan.FromMinutes(60)) 
                 // .And
                 //couch  cach 
                .WithCouchbaseConfiguration("couchedb", clientConfig)
                 .WithJsonSerializer()
                .WithMaxRetries(5)
                 .WithRetryTimeout(100)   
                 .WithCouchbaseCacheHandle("couchedb"); 

            });

                serviceCollection.AddSingleton(CacheInstance);

In my code below ,I use the cache instance to check for a key , if it doesn't exist it's then added to the cache , the _cache.Exists() and _cache.add() are working fine and the the item is added also in the couche db as a json doc (see joined file ) , but the Get function throws a nullReferenceException and I fail to figure out why.

Code snippet :

  if ( _cacheManager.Exists("item_key")  )
       {
        var cachedItem = _cacheManager.Get("item_key"); 
       }
     else                  
       {
   _cacheManager.Add("item_key", "item_value");
       }

Exception : {System.NullReferenceException: Object reference not set to an instance of an object. at CacheManager.Core.Internal.BaseCache1.Get(String key)`

couch data :

snip_couch

Thanks in advance

shamork commented 4 years ago

net4.6.1 is ok, net core not working ? i have the same problem.

shamork commented 4 years ago

After two days digging, found code bellow in NewtonSoft.Json. case JsonContractType.Serializable: this.SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract, member, containerContract, containerProperty); return;

If an object is Serializable, it will serialize it use ISerializable interface. CacheItem implemets ISerializable in net452, but not in netstandard2. Default JsonSerializerSettings in Couchbase.Core.Serialization.DefaultTranscoder which has set CamelCasePropertyNamesContractResolver is ignored. This cause netstandard version generates "key" as net452 version genterates "Key".

Another problem is: there isn't a ctor take a serializer param in CacheManager.Couchbase. So the serializer setting is ignored.

shamork commented 4 years ago

there is two workaroud for this:

  1. Implemets ISerializable in netstandard2. This is a breaking change for netstand2 users!
  2. Catch the expcetion, and reset the cached data, old data will be lost! (recommended)

this problem can only happen after you stored data use one version(net452/netstandard2) and try to get/update data use another version

MichaCo commented 4 years ago

Uh oh OK. Thanks for digging into this and figuring it out man! That's pretty unfortunate.

I actually didn't know that Newtonsoft.Json handles the serializable interface/attribute like this. I knew it respects the properties added to the SerializationInfo.

The fact that it doesn't handle casing properly sounds more like a bug though.

CacheItem has that kind of implementation because the .Core package supports binary serialization in the full framework target. That path is not supported in netstandard because some types like Type are not binary serializable anymore in .NET Core.

Regarding the workarounds, I agree with you. And I also don't have a better solution. I will most likely remove the binary serialization all together in a future version though which will also get rid of the Serializable attribute / interface implementation.