Home > Blockchain >  storing and accessing implementation specific data of a class from generic type of its interface in
storing and accessing implementation specific data of a class from generic type of its interface in

Time:11-16

I'm trying to write a generic cache repository that adds a base key to each data type that is being cached.

    public interface ICacheModel
    {
        private static readonly string _baseKey;
        public static string GetBaseCacheKey()
        {
            return _baseKey;
        }
    }
    public interface ICacheRepository<T> where T : class, ICacheModel
    {
        Task<T> GetAsync(string key);
    }
    public class CacheRepository<T> : ICacheRepository<T> where T : class, ICacheModel
    {
        private readonly IDistributedCache _distributedCache;

        public CacheRepository(IDistributedCache distributedCache)
        {
            _distributedCache = distributedCache;
        }

        public async Task<T> GetAsync(string key)
        {
            var res = await _distributedCache.GetAsync<T>(T.GetBaseCacheKey()   key );
            return res;
        }
    }

I then inherit any object that I plan to cache from ICacheModel and Inject the ICacheRepository anywhere I need to do caching in my project.

but I can't access the static method GetBaseCacheKey() due to compliler error CS0119

Any idea on how I can have this functionality?

Update:


    public class CacheSampleClass : ICacheModel
    {
        public Guid Id { get; set; }
        public string Prop1 { get; set; }
        public string Prop2 { get; set; }

        private static readonly string _baseKey = "CacheSampleClass-";
        public static string GetBaseCacheKey()
        {
            return _baseKey;
        }
    }

this is an example of the inherited classes that I want to cache. I want the GetBaseCacheKey to be unique among all objects that are made from the CacheSampleClass and diffrent from all objects that are made from some other class that inherits ICacheModel

CodePudding user response:

The simplest solution would probably be to just use reflection to get the class name as the "baseCacheKey".

_distributedCache.GetAsync<T>(typeof(T).FullName    key );

Another solution would be to define the base-key as a parameter for your cache object rather than for the type:

public CacheRepository(IDistributedCache distributedCache, string baseKey) 
...

Third solution would be to define a property in the interface:

public interface ICacheModel
{
        public string BaseKey {get;}
}

The implementation of said interface could just provide a implementation of the get method that returns a constant or literal value, so there does not to be any per-object field for this. But the property cannot be static as in the posted example.

Each alternative have some advantages and dissadvantages, so it depends on what you want to do.

CodePudding user response:

Static members in interfaces were introduced in C# 8.0 and I suggest you to avoid using them because interfaces usually should disconnect implementation(field) from contract(method or property).

In your case, its better to convert to regular property

public interface ICacheModel
{
   string GetBaseCacheKey();
}

and move implementation that uses static field to class that implements your interface

CodePudding user response:

You can't implement a static interface method; static members belong to their declaring type only.

You could do something like this instead:

public interface ICacheModelKeyProvider<T> where T : class
{
    string BaseKey { get; }
}

Then in your repository:

public class CacheRepository<T> : ICacheRepository<T> where T : class
{
    private readonly IDistributedCache distributedCache;
    private readonly ICacheModelKeyProvider<T> keyProvider;

    public CacheRepository(
        IDistributedCache distributedCache,
        ICacheModelKeyProvider<T> keyProvider)
    {
        this.distributedCache = distributedCache;
        this.keyProvider = keyProvider;
    }

    public async Task<T> GetAsync(string key)
    {
        var res = await _distributedCache.GetAsync<T>(keyProvider.BaseKey   key);
        return res;
    }
}

Then each model type you create would need a ICacheModelKeyProvider before it could have a repository.

e.g.

public class CacheSampleClass
{
    public Guid Id { get; set; }
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
}

public class CacheSampleClassKeyProvider : ICacheModelKeyProvider<CacheSampleClass>
{
    public string BaseKey { get; } = "CacheSampleClass-"; 
}

Or if you want default behaviour you could even have a generic provider:

public class DefaultKeyProvider<T> : ICacheModelKeyProvider<T>
{
    public string BaseKey { get; } = $"{typeof(T).Name}-"; 
}

And you could even nest that class if it feels neater:

public class CacheSampleClass
{
    public Guid Id { get; set; }
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }

    public class KeyProvider : DefaultKeyProvider<CacheSampleClass> { }
}
  • Related