Unity Injection question passing information to multiple layers

Mar 23, 2011 at 12:00 AM

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>("products"); //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


Mar 23, 2011 at 3:26 AM

To check that I understand your requirement, you want to be able to resolve instances of ICacheProvider with different instance of ICacheManager passed to its constructor based on the name of the cache manager as defined in the config without defining a constructor injection for the registration of ICacheProvider, am I right?  In your code sample which doesn't work, you have a cache manager named "products" so you want to be able to resolve an ICacheProvider passing the cache manager named products, correct?  If this is the case, you can make use of a ParameterOverride:

ICacheProvider productsCache = rootContainer.Resolve<ICacheProvider>(new ParameterOverride("manager", 
                                                                              new ResolvedParameter(typeof(ICacheManager), "products")));

This tells the container to give you an instance of ICacheProvider based on the default registration but overriding the value of the manager parameter with the "products" cache manager.  

Does this answers your concern or am I missing something?

In addition, in your statement "Also, I have tried explicitly defining named registration and I still get the same CacheManager instance back even though two seperate ones are defined", did you define the constructor injection for that registration or did you simply give it a name which differentiates it from the other registration?

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Mar 23, 2011 at 3:32 AM

At first I did not do any constructor injection.  Then I did with a simple string and it worked.  However, the solution was kind of a hack.  The main problem with your scenario is that I have to call ICacheManager out explicitly.  Can this be done via configuration.  Everything we're talking about here is done in an abstracted manner to hide ICacheManger so if I have to call it out explicitly in code, it defeats the purpose.

Mar 23, 2011 at 3:42 AM

Or maybe I could create an extension that might be able to pass the original name parameter?  It seems to me that I should be able to wrap a class and still inject it as I did before, passing the required information further down the chain.

Mar 23, 2011 at 5:44 AM

I see what you mean.  But I don't see yet how the custom extension you mention would work. It seems my initial understanding of what you want to do is incorrect since I thought that you just want to be able to be able to use different instances of cache managers with regards to your MyCacheProvider.  If you don't want to use an ICacheManager, you'll create a new implementation of ICacheProvider, right? And definitely, you need to create a corresponding type registration for that specifying the other class you want to use instead of an ICacheManager.  So what confuses me now is your previous statement "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" as this only pertains to ICacheManagers.  Could you enlighten me on this?

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Mar 23, 2011 at 6:19 AM

Think of it this way.  I configure all my cache stores.  In this case I'm using the entlib caching block.  Let's say there are 5.  I shouldn't have to create a Unity mapping for each one of those to map from my wrapped ICachingProvider to ICachingManager.  If I wasn't wrapping the entlib caching, I would be able to simply say unityContainer.Resolve<ICachingMAnager>("ProductsCache") w/o having an explicit registration because of the Unity entlib extensions.  I'm trying to do the same thing.  Essentially when I call unityContainer.Resolve<ICachingProvider>("ProductsCache"), all I want to do is pass "productsCache" down one level and use it in resolving the second level.  Right now I resolve a ICacheProvider to MyCacheProvider and then the ICacheManager in the constructor is resolved by Unity because of the entlib extensions (it examines the config and adds the necessary resolvers for me).  I want to be able to do the exact same thing, just wrapped with my own interface in case I ever change caching providers.  It's not a crazy concept I don't think.  A custom extension couldn't manage passing that initial "ProductsCache" to the next thing in line?  Since there could be several different cache stores (products, customers, orders...whatever), listing out each different one with an explicit mapping is pretty cumbersome and confusing.

Does that make sense?  I shouldn't have to do double configuration is what I'm saying.  Furthermore, even if I did double configuration, I don't see how it would work seeing as the "name" parameter used to resolve happens at the top layer.  Maybe you could explain how to do this with explicit registration of each separate cache store.

Mar 23, 2011 at 6:51 AM

Furthermore, I would want to do the same thing for many of the blocks.  I could change my underlying services to use different components instead of the entlib so I should be able to make a second hop.  The main problem is that now the entlib documentation say I should be using unity containers to instantiate the entlib objects.  So, I'm using unity to resolve my Ilogger or ICacheProvider interface and THEN I have to use unity again to resolve the actual entlib type because it could be different based on configuration.  It's just easier to do something like CacheFActory.GetCacheManager("") then having to inject it, the docs make it sound like the old factory methods are going away.  This creates a terribly complicated situation when I have a simple scenario.  It's not insane that I don't want to depend on the interface of entlib if I want to change things out, so I wrap them in my own.

Mar 23, 2011 at 7:13 AM
Edited Mar 23, 2011 at 7:18 AM

Yes, it does makes sense.  Sorry, I  misunderstood the problem.  And yes,  I guess a custom extension would solve your requirement as the behavior you want isn't directly supported.

As for your other concern, I don't quite agree.  Yes, it's easier to call the static methods for creating entlib objects but if you're creating it for the purpose of injecting it, doing it through the container is easier.  Since you can have all entlib objects registered in the same container where your ICacheProvider is registered (either by using EnterpriseLibraryContainer.Configurator method or adding the EnterpriseLibraryCoreExtension), this makes it much easier to configure its dependencies to the entlib objects.  It can be easily accomplished using Unity's various injection mechanisms (constructor, property).

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Mar 23, 2011 at 3:06 PM

Any guidance on how to create the custom extension?  There isn't a whole lot of documentation out there.

Mar 23, 2011 at 4:56 PM

I tried the following to no avail.  Seems to me like it should work, but I'm obviously off somewhere

 

    public class WrappedResolver : UnityContainerExtension
    {

        protected override void Initialize()
        {
            
            this.Context.Strategies.Add(new WrappedBuilder(), Microsoft.Practices.Unity.ObjectBuilder.UnityBuildStage.PreCreation);
        }
    }

    public class WrappedBuilder : BuilderStrategy
    {
        public override void PreBuildUp(IBuilderContext context)
        {

            if (context.BuildKey.Type.Name.Contains("ICacheProvider") && context.OriginalBuildKey.Name != null)
            {
                Type mytype = Type.GetType("Microsoft.Practices.EnterpriseLibrary.Caching.ICacheManager, Microsoft.Practices.EnterpriseLibrary.Caching");
                context.AddResolverOverrides(new ParameterOverride("manager", new ResolvedParameter(mytype, context.OriginalBuildKey.Name)));
            }
            base.PreBuildUp(context);
        }

        
    }

Mar 23, 2011 at 5:39 PM
Edited Mar 23, 2011 at 5:40 PM

This approach worked though it kind of feels weird.  

 

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 = Type.GetType("DomainBase.Caching.ICacheProvider, DomainBase");
                context.Existing = baseContainer.Resolve(originalType, new ParameterOverride("manager",
                                                                             new ResolvedParameter(mytype, originalName)));
                
                //context.AddResolverOverrides(new ParameterOverride("manager", new ResolvedParameter(mytype, context.OriginalBuildKey.Name)));
            }
            //base.PreBuildUp(context);
        }   
    }

 

I feel like I should be able to add some general resolver information and then let the system do what it normally does.  Instead I just resolve it right there.  It works, but is it the right approach?  Plus it makes automating all this in configuration a bit more difficult.

Mar 23, 2011 at 9:42 PM
Edited Mar 23, 2011 at 9:45 PM

Also, I'm unsure if that solution will cause recursion issues.
Mar 23, 2011 at 10:57 PM

I unfortunately have to specify the type in a hard coded way in this solution.  If there's a better way of setting mytype, please let me know.

Mar 24, 2011 at 12:52 AM

And as I dig further, I'm finding that a child container may be a far easier approach to this where instead of sending a name, I use an entirely different container to resolve the custom stuff.

Mar 29, 2011 at 4:20 AM
Edited Mar 29, 2011 at 4:58 AM

This code lets you avoid hardcoding the parameter type.  However, you would still need to have some assumptions about the type of the parameter which you want to configure, for example you may have established that your ICacheProvider implementations will always have a single interface parameter:

IPolicyList resolverPolicyDestination;
IConstructorSelectorPolicy selector = context.Policies.Get(context.BuildKey, out resolverPolicyDestination);

SelectedConstructor selectedCtor = selector.SelectConstructor(context, resolverPolicyDestination);

ParameterInfo[] parameters = selectedCtor.Constructor.GetParameters();

for (int ctr = 0; ctr < parameters.Length; ctr++)
{
                Type parameterType = parameters[ctr].ParameterType;
                if (parameterType.IsInterface) // assumption about what is the parameter you want to configure a parameteroverride for
                {
                    context.AddResolverOverrides(new ParameterOverride(parameters[ctr].Name,
                            new ResolvedParameter(parameterType, context.BuildKey.Name)));
                }
}
 

This is only better than hard-coding if you indeed have a standard rule about the type of parameter of your ICacheProvider implementations.

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Mar 31, 2011 at 11:47 PM

Is that code correct?  context.Policies.Get requires a Type parameter in order to build properly

Apr 1, 2011 at 12:26 AM

did you mean:

context.Policies.Get<IConstructorSelectorPolicy>(context.BuildKey, out resolverPolicyDestination);
tried that and it didn't work.  slectedCtor came back null even though I only have one that takes an ICacheManager.  

Apr 1, 2011 at 1:24 AM

Yes, you're right about the code, sorry about the typo.   What is the value of the context.BuildKey.Type when you got the null selectedCtor?  In addition, what UnityBuildStage did you use when you add your custom strategy?

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Apr 1, 2011 at 1:46 AM

value of context.BuildKey.Type is ICacheProvider

build stage is precreation.  If I make it any later, it errors out before my extension every fires

Apr 1, 2011 at 2:16 AM

I suggest you put it in the TypeMapping stage.  The context.BuildKey.Type you will get then should be the type of the actual implementation of the interface.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Apr 1, 2011 at 2:20 AM

I will give that a try, but it errors out right after pre creation because I'm trying to resolve by name container.Resolve<ICacheProvider>("cache_name_here").  Since there is no named mapping, it errors out.  By the time you get to TypeMapping, isn't the original build key gone?

Apr 1, 2011 at 2:58 AM

I misunderstood your requirement.  I thought what you want is when you resolve for a named mapping, you want to resolve its dependency using the same name. 

So now let me confirm what you want, say you have 2 cache managers, one is named "Cache Manager" and the other named "Product Cache".

In your unity container, you only configured a default type mapping for ICacheProvider mapped to MyCacheProvider.  And when you try to do this

container.Resolve<ICacheProvider>("Product Cache")

you want to get an instance of MyCacheProvider which uses the "Product Cache" cache manager, is this correct?

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Apr 1, 2011 at 3:02 AM

Well, I'd like the ability to specify different cache stores for different named registrations.  So it's kind of both.  However, yes, I want to be able to resolve "Product Cache" to the product cache defined in my entlib caching section as if I were doing Entlibcontainer.current.GetInstance<>("Product Cache").  The only way I could figure to do this was to pass the cache name all the way down as I'm wrapping ICacheManager with ICacheProvider.

Apr 1, 2011 at 3:08 AM

Here is the main goal: to be able to resolve a cache manager like I normally would if I was resolving CacheManager directly.  However, I'm wrapping it so that I can swap the caching provider out later.  That's all

Apr 1, 2011 at 3:31 AM

I see.  What I could think of is since you're trying to resolve for a named mapping which you didn't actually registered, you can consider adding the registration in the PreBuildUp method (if the registration doesn't exists yet) by adding an instance of IBuildKeyMappingPolicy in the context.PersistentPolicies object.  Is this an acceptable solution for you?

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Apr 1, 2011 at 3:34 AM

The mapping does exist because I configure the unity container with the entlib unity extensions.  I'm not sure exactly what you're referring to.  Can you provide a short sample.

Apr 1, 2011 at 3:38 AM

I meant the named mapping for the ICacheProvider.  That's what I understood from your previous statement:

"I will give that a try, but it errors out right after pre creation because I'm trying to resolve by name container.Resolve<ICacheProvider>("cache_name_here").  Since there is no named mapping, it errors out."

This gives me the impression that you didn't have (and didnt' want to have to explicitly have) a type mapping for ICacheProvider named "cache_name_here" but would still like to be able to resolve such type using the cache manager named "cache_name_here" as the parameter to the constructor of the implementation of ICacheProvider.

If this isn't what you want, kindly post the sample code and configuration demonstrating what you want.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Apr 1, 2011 at 3:42 AM

This is correct.  

This gives me the impression that you didn't have (and didnt' want to have to explicitly have) a type mapping for ICacheProvider named "cache_name_here" but would still like to be able to resolve such type using the cache manager named "cache_name_here" as the parameter to the constructor of the implementation of ICacheProvider.

I don't want to explicitly make a mapping for "cache_name_here" as that mapping is already put in place by the entlib unity extension.  I don't want to define it twice.  I want to resolve as if it's already there because it is already there.

Apr 1, 2011 at 3:52 AM
Edited Apr 1, 2011 at 4:01 AM

Then is my last suggestion acceptable?

"What I could think of is since you're trying to resolve for a named mapping which you didn't actually registered, you can consider adding the registration in the PreBuildUp method (if the registration doesn't exists yet) by adding an instance of IBuildKeyMappingPolicy in the context.PersistentPolicies object. "

This is just an idea, I'm still playing around the policy list of the context object to see how to get the details necessary to create the registration for the named mapping.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Apr 1, 2011 at 4:20 AM

I guess my answer to that is that I'm not sure since I'm not familiar with that.  If you had a code sample that would be helpful.  I will give it a try.

Apr 1, 2011 at 4:31 AM
Edited Apr 1, 2011 at 4:31 AM

I posted a sample code here.  It doesn't perform your exact requirements but the idea of checking if the type mapping exists is there as well as registering the type mapping you need thru setting a policy in the context.PersistentPolicies object. 

By the way, the strategy would have to be added at the Setup UnityBuildStage.  Adding it at any later stage won't work.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Apr 1, 2011 at 5:40 AM

I looked at the sample and I guess I don't understand how it would help.  How is this different than just hardcoded a named registration?  Sure I can check if the mapping exists, but if it doesn't, I have to hardcode it in to resolve.  My whole point is the type mapping already exists.  It's just done automatically by the enterprise library unity extensions.  I'm just one layer deeper than normal because I wrapped the entlib caching in my own interface.  I'll give it a deeper look tomorrow, but I don't really see how it helps.

Apr 1, 2011 at 8:24 AM
Edited Apr 1, 2011 at 9:11 AM

I'm not sure if you don't like the whole idea of registering the type mapping in the strategy or if it's just the hardcoding part.  If it's the hardcoding part, you can somehow get around that.  The sample code below shows how although it definitely has rooms for improvement especially the logic of getting the parameter information.  I particularly not satisfied with that since it required me to create a different copy of an IBuilderContext just so I could supply the correct BuildContext property (If you don't understand this, try debugging through the IConstructorSelectPolicy.SelectConstructor method).  The code also assumes that the first parameter (parameters[0]) is the one which we would add a resolver override for.  There is surely other better ways of doing these things but this is what I have right now.

if (context.BuildKey.Type.Name.Contains("ICacheProvider"))
{
            //check if a mapping exists already
            IBuildKeyMappingPolicy currentMappingToResolve = context.Policies.Get(context.OriginalBuildKey);

            if (currentMappingToResolve == null)
            {
                 //get info about the default mapping
                 IBuildKeyMappingPolicy defaultMapping = context.Policies.Get(new NamedTypeBuildKey(context.OriginalBuildKey.Type));

                 //get this info so we could derive the actual implementation (MyCacheProvider) mapped to the interface
                 NamedTypeBuildKey defaultImplementation = defaultMapping.Map(context.OriginalBuildKey, context);

                //add the mapping with the context.OriginalBuildKey.Name as the registration name
                 context.PersistentPolicies.Set(
                        new BuildKeyMappingPolicy(new NamedTypeBuildKey(defaultImplementation.Type)),
                         new NamedTypeBuildKey(context.BuildKey.Type, context.OriginalBuildKey.Name));

                 IPolicyList resolverPolicyDestination;
                 IConstructorSelectorPolicy selector = context.Policies.Get(defaultImplementation, out resolverPolicyDestination);

                 IBuilderContext contextCopy = new BuilderContext(context.Strategies, context.Lifetime, context.PersistentPolicies, context.Policies, new NamedTypeBuildKey(defaultImplementation.Type, context.OriginalBuildKey.Name), context.Existing);
                  SelectedConstructor selectedCtor = selector.SelectConstructor(contextCopy, resolverPolicyDestination);

                  ParameterInfo[] parameters = selectedCtor.Constructor.GetParameters();

                  Type parameterType = parameters[0].ParameterType;
                  if (!parameterType.IsValueType)
                  { 
                     context.AddResolverOverrides(new ParameterOverride(parameters[0].Name, new ResolvedParameter(parameterType, context.BuildKey.Name)));
                  }
}
base.PreBuildUp(context);

 

This is just a sample. the key is to explore the objects which you could use from the IBuilderContext object.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Apr 1, 2011 at 4:24 PM

Yeah, it just seems redundant to have to re-register a type mapping that already exists in memory.  This solution then isn't a whole lot different than what I posted earlier.

Apr 1, 2011 at 5:05 PM

This same scenario exists if I was trying to resolve a database object as well.  It would work well if I just wanted the default database, but if I wanted a named one, I would run into the same issue.

Apr 3, 2011 at 7:00 AM
Edited Apr 3, 2011 at 7:02 AM

I think the type mapping you're referring to which "already exists in memory" is the type mapping for the enterprise library type, ICacheManager.  The code in the custom strategy doesn't re-register that, it registers the type mapping for your wrapper class, ICacheProvider.

This is what I suggested since what you want is to be able to resolve a mapping which you didn't actually register (only the registration for the ICacheManager exists).  So either you register the mapping or create the object directly within the custom strategy.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Apr 4, 2011 at 4:47 PM

At what point does this run: Setup, PreCreation, TypeMapping?  I'm not entirely sure what it's doing.

Apr 4, 2011 at 7:22 PM

Also, I don't think your sample works

If you have a default mapping then currentMappingToResolve is not null and doesn't drop in.  If you don't have a default mapping then currentMappingToResolve  AND defaultMapping is null causing a nullreference.

I'm going to poke around a bit more, but I've tried several different routes all to no avail.  What I have for a stop gap solution is passing the cache name as a string and calling resolve manually TWICE.

Apr 4, 2011 at 7:25 PM

I also had to define a named registration for every single cache type essentially doing redundant configuration.  This is quite a shame.

Apr 5, 2011 at 1:49 AM

It runs at Setup.  In addition, the currentMappingToResolve will be null even if you have a default mapping if you're resolving for a named registration of a type which doesn't exists.  If you want, I can send you a sample solution which uses this. Just email me at entlib.support@avanade.com.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com