I have abstract base class like:
public abstract class CacheValueProviderBase<T> where T : ICacheItem
{
protected ConcurrentDictionary<int,T> dataList = new ConcurrentDictionary<int, T>();
public virtual void Add(T model){ // add code }
public virtual bool Remove(int id){ //remove code }
public abstract string getName();
public abstract void UpdateForceFromDataBase();
public abstract void UpdateForceFromCacheServer();
public virtual bool allowForUpdater
{
get
{
return true;
}
}
public virtual bool beforeUpdate()
{
return true;
}
}
I have multiple derived classes from this abstract class. The Slider_CacheValueProvider
class below is used as an example.
public class Slider_CacheValueProvider : CacheValueProviderBase<Cache_Home_Slider_Model>
{
public override string getName()
{
return "Slider_Cache";
}
public override void UpdateForceFromCacheServer()
{ // updating from cache server
}
public override void UpdateForceFromDataBase()
{ // updating from database
}
}
Slider cache model:
public class Cache_Home_Slider_Model : ICacheItemID
{
public int ID { get; set; }
public string SlideImage { get; set; }
public string Link { get; set; }
}
All cache models depends ID property and implement this interface just for easy crup operation:
public interface ICacheItemID
{
int ID { get; set; }
}
Info: My caching mechanism has 2 steps. The first step is an internal cache. The second step is an external cache server.
I have cache updater. It is updating caches periodically which depends abstract class 'allowForUpdater' property. Firstly, I found all derived classes with this:
public static List<Type> CacheTypeList()
{
var type = typeof(CacheValueProviderBase<>);
return Assembly.GetExecutingAssembly().GetTypes().Where(i => !i.IsAbstract && !i.IsInterface &&
i.BaseType != null && i.BaseType.IsGenericType && i.BaseType.GetGenericTypeDefinition() == type
).ToList();
}
And iterating like this:
foreach (var item in CacheTypeList())
{
var cache= getCache(item);
if(cache.allowForUpdater && cache.beforeUpdate())
{
cache.UpdateForceFromCacheServer();
}
}
And the getCache
method:
public static CacheValueProviderBase<ICacheItem> getCache(Type type)
{
var val = storeList.Find(i => i.Key == type).Value;
return (CacheValueProviderBase<ICacheItem>)val;
}
storeList is static list and include Slider_CacheValueProvider global on app.
The problem is the getCache
method. When I try to cast it, I receive an exception. 'Unable to cast object of type ...' . Slider_CacheValueProvider inherited from base and slider model implement from ICacheItem. What is the problem? Why can't I cast?
Update 1:
Using 'out' keyword to abstract class, getting this error: 'Invalid variance modifier. Only interface and delegate type parameters can be specified as variant'.
So i change the abstract class with interface. Interface is :
public interface ICacheProvider<T> where T : ICacheItemID
{
DateTime LastModifiedTime { get; set; }
void Add(T model);
bool Remove(int id);
bool Update(T model);
T Where(Func<T, bool> expression);
void Clear();
int Count(int id);
IEnumerable<T> GetList();
void AddList(IEnumerable<T> model);
void RemoveList(IEnumerable<int> model);
void RemoveByFunc(Func<KeyValuePair<int, T>, bool> expression);
IEnumerable<T> WhereList(Func<T, bool> expression);
string getName();
void UpdateForceFromDataBase(bool updateCache = true);
void UpdateForceFromCacheServer();
bool allowForUpdater { get; }
bool beforeUpdate();
}
And current abstract class like this:
public abstract class CacheValueProviderBase<T> : ICacheProvider<T> where T : ICacheItemID
If i change interface to 'out T', getting error on Add,Update,AddList,RemoveByFunc. Error is:
"Invalid variance, The Type parameter 'T' must be contravariantly valid on ICacheProvider.Add(T) (or other method name) 'T' is a covariant. "
Update 2:
I changed my code. I created new interface for updater like this:
public interface ICacheUpdaterImplements
{
string getName();
void UpdateForceFromDataBase();
void UpdateForceFromCacheServer();
bool allowForUpdater();
}
I get this interface like this:
public static ICacheUpdaterImplements getCacheUpdaterImplements(Type type)
{
return (ICacheUpdaterImplements)storeList.Single(i => i.Key == type).Value;
}
And change updater code like this:
foreach (var item in CacheTypeList())
{
var updater= getCacheUpdaterImplements(item);
if(updater.allowForUpdater())
{
updater.UpdateForceFromCacheServer();
}
}
So, I see, I have wrong design. I changed code and resolve problem.