Search code examples
.netdependency-injectionunity-containerenterprise-library

Unity Injection question passing information to multiple layers


I'm trying to used Unity to do some dependency injection with some cross-cutting classes. Right now I have some caching infrastructure that I've wrapped around the MS enterprise library. I'm wrapping it because I may want to use something else later. Furthermore, I may want to mix and match for different cache stores.

So I have a generic caching interface and a concrete implementation as follows

public interface ICacheProvider
{
    void Add(object key, object value);

    void Remove(object key);

    object GetData(object key);

    void Flush();
}

public class MyCacheProvider : ICacheProvider
{
    private ICacheManager cacheManager;

    public MyProvider(ICacheManager manager)
    {
        cacheManager = manager;
    }
}

The ICacheManager is of the type in the enterprise library. What I'm trying to do is use unity to resolve the cache manager at runtime. Furthermore, this could be different for different cacheStores. Right now, they all resolve to the type I've displayed. Here's my unity config.

<unity>
<typeAliases>
  <typeAlias alias="string" type="System.String, mscorlib" />
  <typeAlias alias="ICacheProvider" type="DomainBase.Caching.ICacheProvider, DomainBase" />
  <typeAlias alias="MSCacheProvider" type="Caching.MyCacheProvider, Caching" />
</typeAliases>
<containers>
  <container>
    <types>
      <register type="ICacheProvider" mapTo="MSCacheProvider" />
    </types>
  </container>
</containers>

Here's how I setup my unity container:

 private IUnityContainer rootContainer;

 rootContainer = new UnityContainer().LoadConfiguration();
 var configurator = new UnityContainerConfigurator(rootContainer);
 EnterpriseLibraryContainer.ConfigureContainer(configurator, ConfigurationSourceFactory.Create());

This allows the enterprise library to automatically pick up information from the config and resolve based on what's there. The problem is that when I go two levels deep (ie - I'm not resolving directly to enterprise library interface), the resolution fails because I don't have a named resolution. Named resolution isn't needed given the enterprise library extensions since the provided extensions register the proper resolution as if it were named. Though, I gather Unity cannot make that magic leap on it's own from mapping my interface to the enterprise library. Here's some sample code

//note that's ICacheProvider which my interface for abstracting caching.  NOT
//the enterprise library interface
ICacheProvider customersCache = rootContainer.Resolve<ICacheProvider>(); //works
ICacheProvider productsCache = rootContainer.Resolve<ICacheProvider>("ProductsCache"); //doesn't work

The first sample works because there's default resolution, however the second sample fails because I don't have a named resolution for "products". Is there a good way to deal with this situation? I don't want to have to have named resolution for every different cache store I add to the system as they're already defined in the caching config. I would try a different type of injection, but you pretty much have to use constructor injection in this scenario.

Also, I have tried explicitly defining named registration and I still get the same CacheManager instance back even though two seperate ones are defined. I suppose once the initial resolution is done, it looks at the constructor and has no "name" left and just injects based on the default?

Thanks for your help


Solution

  • Found the answer to my own question. The only way I could figure it out was to use a custom extension and check for type. Here's a sample

     public class WrappedResolver : UnityContainerExtension
    {
    
        protected override void Initialize()
        {
            this.Context.Strategies.Add(new WrappedBuilder(this.Container), Microsoft.Practices.Unity.ObjectBuilder.UnityBuildStage.PreCreation);
    
        }
    }
    
    public class WrappedBuilder : BuilderStrategy
    {
        IUnityContainer baseContainer;
    
        public WrappedBuilder(IUnityContainer container)
        {
            baseContainer = container;
        }
    
        public override void PreBuildUp(IBuilderContext context)
        {
    
            if (context.BuildKey.Type.Name.Contains("ICacheProvider") && context.OriginalBuildKey.Name != null)
            {
                string originalName = context.OriginalBuildKey.Name;
                Type mytype = Type.GetType("Microsoft.Practices.EnterpriseLibrary.Caching.ICacheManager, Microsoft.Practices.EnterpriseLibrary.Caching");
                Type originalType = context.BuildKey.Type;// Type.GetType("DomainBase.Caching.ICacheProvider, DomainBase");
                context.Existing = baseContainer.Resolve(originalType, new ParameterOverride("manager",
                                                                             new ResolvedParameter(mytype, originalName)));
    
                context.BuildComplete = true;
    
            }
            base.PreBuildUp(context);
        }   
    }