Search code examples
unity-containercachemanager

Is it possible to use ICacheManager<> with different type of configurations at the same time?


Imagine that I have interfaces like below which all inherits from ICacheManager<>

public interface ICacheManagerRuntime<T> : ICacheManager<T>
public interface ICacheManagerRedis<T> : ICacheManager<T>
public interface ICacheManagerRedisWithRuntime<T> : ICacheManager<T>

I want to inject ICacheManager{CacheType} interfaces to implemantation of Cache classes like:

CacheRuntime, CacheRedis, CacheRedisWithRuntime

With unity I want to inject them like below:

container.RegisterType<ICacheManagerRuntime<object>>(
                new ContainerControlledLifetimeManager(),
                new InjectionFactory((c, t, n) =>
                {
                    return CacheFactory.Build... // Return CacheManager just with RuntimeCacheHandle
                })));


container.RegisterType<ICacheManagerRedis<object>>(
                new ContainerControlledLifetimeManager(),
                new InjectionFactory((c, t, n) =>
                 {
                    return CacheFactory.Build... // Return CacheManager just with RedisCacheHandle
                })));


container.RegisterType<ICacheManagerRedisWithRuntime<object>>(
                new ContainerControlledLifetimeManager(),
                 {
                    return CacheFactory.Build... // Return CacheManager just with RuntimeCacheHandleWithRedisBackPlane
                })));

What ever I have done, I am getting this exception:

An unhandled exception of type 'Microsoft.Practices.Unity.ResolutionFailedException' occurred in Microsoft.Practices.Unity.dll

Additional information: Resolution of the dependency failed, type = "Solid.Play.Business.Interfaces.IProductService", name = "(none)".

Exception occurred while: Resolving parameter "cache" of constructor Solid.Play.Cache.Caches.CacheRuntime(Solid.Play.Cache.Interfaces.ICacheManagerRuntime`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] cache).

Exception is: InvalidCastException - Unable to cast object of type 'CacheManager.Core.BaseCacheManager`1[System.Object]' to type 'Solid.Play.Cache.Interfaces.ICacheManagerRuntime`1[System.Object]'.

-----------------------------------------------

At the time of the exception, the container was:



  Resolving Solid.Play.Business.Services.ProductService,(none) (mapped from Solid.Play.Business.Interfaces.IProductService, (none))

    Resolving Solid.Play.Cache.Interception.CachingInterceptorBehavior,(none)

    Resolving parameter "cache" of constructor Solid.Play.Cache.Interception.CachingInterceptorBehavior(Solid.Play.Cache.Interfaces.ICacheSolid cache)

      Resolving Solid.Play.Cache.Caches.CacheSolid,(none) (mapped from Solid.Play.Cache.Interfaces.ICacheSolid, (none))

      Resolving parameter "cacheRuntime" of constructor Solid.Play.Cache.Caches.CacheSolid(Solid.Play.Cache.Interfaces.ICacheRuntime cacheRuntime, Solid.Play.Cache.Interfaces.ICacheRedis cacheRedis, Solid.Play.Cache.Interfaces.ICacheRedisWithRuntime cacheRedisWithRuntime)

        Resolving Solid.Play.Cache.Caches.CacheRuntime,(none) (mapped from Solid.Play.Cache.Interfaces.ICacheRuntime, (none))

        Resolving parameter "cache" of constructor Solid.Play.Cache.Caches.CacheRuntime(Solid.Play.Cache.Interfaces.ICacheManagerRuntime`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] cache)

Solution

  • The cast does not work because you are trying to cast an instance to an interface it does not implement. Simplified, what you are trying looks like this:

    public interface IBase
    {
    }
    
    public interface ISub : IBase { }
    
    public class BaseClass : IBase
    {
    }
    
    var sub = (ISub)new BaseClass();
    

    If you want to inject different kind of instances of the same interfaces, the Unity DI framework provides a way to do that via named injection.

    Example:

            container.RegisterType<ICacheManager<object>>("runtimeCache",
            new ContainerControlledLifetimeManager(),
            new InjectionFactory((c, t, n) =>
            {
                return CacheFactory.Build<object>(s =>
                {
                    s.WithSystemRuntimeCacheHandle("cache.runtime");
                });
            }));
    
            container.RegisterType<ICacheManager<object>>("redisCache",
            new ContainerControlledLifetimeManager(),
            new InjectionFactory((c, t, n) =>
            {
                return CacheFactory.Build<object>(s =>
                {
                    s.WithRedisConfiguration("cache.redis", config =>
                    {
                        config
                        .WithAllowAdmin()
                        .WithDatabase(0)
                        .WithEndpoint("localhost", 6379);
                    })
                    .WithRedisCacheHandle("cache.redis");
                });
            }));
    

    To resolve the first one, you'd use

    var runtimeCache = container.Resolve<ICacheManager<object>>("runtimeCache");
    

    You can inject the ICacheManager interface to constructors with attributes for example.

    public YourClass([Dependency("runtimeCache")] ICacheManager<object> cache)
    {
    
    }