Can't resolve when using RegisterType but can when using RegisterInstance

Jan 22, 2009 at 5:52 PM
It would be hard for me to explain my question so I've made tests which behavior I don't understand.  I am using Unity v 1.2.
test Resolve_Of_Interface_Registered_With_RegisterType  is failling.
test Resolve_Of_Interface_Registered_With_RegisterInstance is passing.

The difference is that first one is registering type and second one is registering instance. Can you please tell me if this is correct behavior and why it suppose to behave like this.

Thank you


    [TestFixture]
    public class UnityResolvingTest
    {

        [Test]
        public void Resolve_Of_Interface_Registered_With_RegisterType()
        {
            // SETUP testing scenario
            UnityContainer container = new UnityContainer();
            
            container.RegisterType<IDemandedInterface, IIntermediateInterface>();
            container.RegisterType<IIntermediateInterface, TypeOfInterface>();

            // EXECUTE code under test (examined behaviour of tested object)
            IDemandedInterface instance = container.Resolve<IDemandedInterface>();

            // CHECK results (verify expectations)
            Assert.That(instance, Is.TypeOf(typeof(TypeOfInterface)));
        }

        [Test]
        public void Resolve_Of_Interface_Registered_With_RegisterInstance()
        {
            // SETUP testing scenario
            UnityContainer container = new UnityContainer();
            
            container.RegisterType<IDemandedInterface, IIntermediateInterface>();
            container.RegisterInstance<IIntermediateInterface>(new TypeOfInterface());

            // EXECUTE code under test (examined behaviour of tested object)
            IDemandedInterface instance = container.Resolve<IDemandedInterface>();

            // CHECK results (verify expectations)
            Assert.That(instance, Is.TypeOf(typeof(TypeOfInterface)));
        }

    }


    public class TypeOfInterface : IIntermediateInterface
    {
        
    }


    public interface IDemandedInterface
    {
        
    }


    public interface IIntermediateInterface : IDemandedInterface
    {

    }
Jan 22, 2009 at 6:24 PM
Thanks, the tests showed exactly what you're seeing and what you expected. Very clear.

Having said that, what you're seeing is expected behavior.

The fundamental issue is that type mapping happens once, and Unity doesn't do transitive type mapping. So in your RegisterType case, it maps IDemandedInterface to IIntermediateInterface, and then goes on trying to build up IIntermediateInterface (and fails, since it's an interface).

In the RegisterInstance case, the reason it works is because the type mapping happens as before, mapping IDemandedInterface to IIntermediateInterface. The next step is Lifetime management. It goes looking for a lifetime manager registered for IIntermediateInterface, and finds it, becuase you registered an instance under that interface.

May I ask what you're trying to achieve with the chain of interfaces approach? I would expect things to be much more maintainable if you just map IDemandedInterface directly to the implementing type.

Jan 23, 2009 at 9:34 AM
Thank you for your answer.

The reason why I am trying to use it like this is that we have inheritance hierarchy of interfaces with some special classes that provides context information. Objects that need this information usually depends on some part of interface that holds only the part of information that they need.  But when you register the information we don't want to register it one by one.

I've made some example that is similar ....
ICurrentlyLoggedSession  is the top interface. And some classes need only ICurrentUserEmail, some need ICurrentUserSetting etc....
This worked well when we've used RegisterInstance but broke when using RegisterType. I guess that it's not possible to extend unity to do the transitive mapping with little effort, right ?

public interface ICurrentlyLoggedSession : ICurrentlyLoggedUser, ICurrentUserSetting
{
}


public interface ICurrentlyLoggedUser : ICurrentUserEmail
{
   string FirstName {get;}
   string LastName {get; }
}

public interface ICurrentUserSetting
{
   IUserScheme Scheme {get;}
}

public interface ICurrentUserEmail
{
       string Email {get;}
}
Jan 24, 2009 at 5:42 AM
I am experiencing what I believe may be a similar issue that perhaps someone could provide guidance on... My situation is like this:

interface ILookup<T>
{
     T Get(int id)
}

interface ICompositeLookup<T>
{
    void AddLookup(ILookup<T> lookup)
}

class CompositeLookup : ILookup<Variable>, ICompositeLookup<Variable>
{
    Variable Get (int id)
    {
    }
    void AddLookup(ILookup<Variable> lookup)
    {
    }
}

interface IExpressionEvaluator
{
    Variable EvaluateExpression(string expr)
}

class ExpressionEvaluator : IExpressionEvaluator
{
    ExpressionEvaluator(ILookup<Variable> variables) {}

    Variable EvaluateExpression(string expr)
    {
    }
}

type mappings in config file:

<type name="ConstraintVariableLookup"
                type="Lookups.ICompositeLookup`1[[Lookups.Variable, Lookups]], Lookups"
                mapTo="Lookups.CompositeVariableLookup, Lookups">
    <lifetime type="singleton" />
</type>

<type type="Lookups.IExpressionEvaluator, Lookups"
                mapTo="Lookups.ExpressionEvaluator, Lookups">
            <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
              <constructor>
                <param name="variables"
                       parameterType="Lookups.ILookup`1[[Lookups.Variable, Lookups]], Lookups">
                  <dependency name="ConstraintVariableLookup"/>
                </param>
            </typeConfig>
</type>

With the configuration and classes / interfaces defined above when I attempt to perform a:

unity.Resolve<IExpressionEvaluator>( )

I get an error along the lines of   " is an interface and cannot be constructed. Are you missing a type mapping?" on the variables parameter to ExpressionEvaluator.

Even though the dependency named ConstraintVariableLookup actually implements ILookup<Variable> as well as ICompositeLookup<Variable> it can not be resolved as such and blows up with this error. Is there away I can add another alias / mapping or something to allow the named dependency ConstraintVariableLookup to also be resolved by objects looking for dependcies on ILookup<Variable>???
Jan 24, 2009 at 8:09 AM
That question I can answer. You can add another mapping by adding another mapping. You can map as many interfaces as you like to the same concrete class. Just be sure you only set the lifetime on one of the registrations and you'll be fine (it won't blow up with multiple lifetime managers, but the last one given wins).

So, for your example, add this:

<type name="ConstraintVariableLookup"
                type="Lookups.ICompositeLookup`1[[Lookups.Variable, Lookups]], Lookups"
                mapTo="Lookups.CompositeVariableLookup, Lookups">
    <lifetime type="singleton" />
</type>
<type name="ConstraintVariableLookup"
                type="Lookups.ILookup`1[[Lookups.Variable, Lookups]], Lookups"
                mapTo="Lookups.CompositeVariableLookup,Lookups" />

and you're set.

If you wanted the container to automatically register for all the interfaces a class implements, you'd need to write a small container extension to do it.

Jan 24, 2009 at 9:43 PM

If the first registration in your example also had constructor dependencies defined in the config file, would you have to duplicate that information in the second registration, or not?

From: ctavares [mailto:notifications@codeplex.com]
Sent: Saturday, January 24, 2009 3:10 AM
To: Scott Walker
Subject: Re: Can't resolve when using RegisterType but can when using RegisterInstance [unity:44959]

From: ctavares

That question I can answer. You can add another mapping by adding another mapping. You can map as many interfaces as you like to the same concrete class. Just be sure you only set the lifetime on one of the registrations and you'll be fine (it won't blow up with multiple lifetime managers, but the last one given wins).

So, for your example, add this:

<type name="ConstraintVariableLookup"
type="Lookups.ICompositeLookup`1[[Lookups.Variable, Lookups]], Lookups"
mapTo="Lookups.CompositeVariableLookup, Lookups">
<lifetime type="singleton" />
</type>
<type name="ConstraintVariableLookup"
type="Lookups.ILookup`1[[Lookups.Variable, Lookups]], Lookups"
mapTo="Lookups.CompositeVariableLookup,Lookups" />

and you're set.

If you wanted the container to automatically register for all the interfaces a class implements, you'd need to write a small container extension to do it.

Read the full discussion online.

To add a post to this discussion, reply to this email (unity@discussions.codeplex.com)

To start a new discussion for this project, email unity@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com

Jan 24, 2009 at 10:52 PM
Nope, no need to repeat injection configuration. All the information you specify in a <type> element, other than the type mapping, is applied to the concrete class, not the interface. If that makes any sense.

Jan 24, 2009 at 10:57 PM

Wow that was a fast reply for a weekend!  Here I thought I was the only one who was a workaholic! 

Anyway, that answer makes perfect sense and is exactly what I was hoping for!  Thanks again, you saved me some hours of frustration!

From: ctavares [mailto:notifications@codeplex.com]
Sent: Saturday, January 24, 2009 5:53 PM
To: Scott Walker
Subject: Re: Can't resolve when using RegisterType but can when using RegisterInstance [unity:44959]

From: ctavares

Nope, no need to repeat injection configuration. All the information you specify in a <type> element, other than the type mapping, is applied to the concrete class, not the interface. If that makes any sense.

Read the full discussion online.

To add a post to this discussion, reply to this email (unity@discussions.codeplex.com)

To start a new discussion for this project, email unity@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com