2

I'm using MS Unity and 'Registration by convention' (auto-registration) to register all the classes in a namespace. The code (seen below) works at it should and returns an expected result

var container = new UnityContainer();
        container.RegisterTypes( AllClasses.FromLoadedAssemblies().Where(
            t => t.Namespace == "DependencyInjectionExample.Test"), 
            WithMappings.FromMatchingInterface, 
            WithName.Default, 
            WithLifetime.ContainerControlled);

Result

Container has 4 registrations

  • IUnityContainer '[default]' Container
  • TestClass1 '[default]' ContainerControlled
  • TestClass2 '[default]' ContainerControlled
  • TestClass3 '[default]' ContainerControlled

My problem is that I can't figure out how to resolve them. I've tried with

var allRegistered = container.ResolveAll<ITestable>();

But it doesn't return anything (all the testclasses implement ITestable). When I try

var singleRegistered = container.Resolve<ITestable>();

I get a 'ResolutionFailedException' - "The type ITestable does not have an accessible constructor". I've read that it is because that the registered types are not named, but that is not possible when using auto-registration.

What should I do to resolve the registered types?

EDIT

namespace DependencyInjectionExample.Test
{
 public interface ITestable
 {
    string SaySomething();
 }
}

One of the three test classes. They all do the same thing.

namespace DependencyInjectionExample.Test
{
 public class TestClass1 : ITestable
 {
    public TestClass1() { }

    public string SaySomething()
    {
        return "TestClass1 hello";
    }
 }
}
barto90
  • 679
  • 1
  • 9
  • 27
  • If you take a closer look, you will see that only concrete classes are registered within the container. The only interface registered in the container is the default IUnityContainer reference. Are you implementing interfaces in those classes (TestClass1, etc)? Also, you can't have more than one class registered for the same interface, so if you're implementing ITestable in those three TestClasses it won't work. Post the code for the classes registered in the container so we can understand better what's going on. – Denis Brat Feb 19 '14 at 18:55
  • If I only can have one class registered for an Interface, then what is the point? I want to be able to add classes which implement an interface to the namespace and then automatically resolve them. For example by getting a list of the concrete classes. – barto90 Feb 19 '14 at 21:01
  • That is why you have a method for registering each interface with a name. If you want to have 3 different instances for a single interface, then you have to register each one manually using that method. Sorry, but this is the only way you will achieve what you want. _You may want to take a look on MEF_. It comes with the framework and I think it supports what you need out of the box. – Denis Brat Feb 19 '14 at 21:45
  • Another way is to create an attribute that enables you to specify the name of the instance, then you just have to hook in the Unity build policies and register that specific instance with the name given in the attribute. Sounds amazingly worth to try. I may take a look at it once I get to home (in office right now). – Denis Brat Feb 19 '14 at 21:47
  • I'm a bit confused now. What is the Registration by convention used for if it can't register types implementing an interface? Thank you, I'm really looking forward to your solution. – barto90 Feb 20 '14 at 08:01
  • The registration by convention is meant to be used in simple scenarios. Some specific cases, like registrations with constructor injection, are not that easy to handle. Sorry mate. I didn't had the time to take a look at this for you. In the mean time you could avoid registration by convention under the namespaces of the conflicting classes and use the explicit registration. I'll try my best to find some time to fiddle with this. Maybe tomorrow I'll be able to work a solution for you! – Denis Brat Feb 21 '14 at 01:46
  • I'm looking for a solution like this http://stackoverflow.com/a/7483905/1636080 but with the use of registration by convention. The problem is still to register multiple classes with the same interface without naming them. – barto90 Feb 21 '14 at 06:57
  • Without specifying a name you won't be able to accomplish what you want. I'll take a look at that attribute I told you, but be aware that you will have to resolve the instances by name once you use it. – Denis Brat Feb 21 '14 at 17:35
  • So maybe I should use reflection to get the classes implementing the interface to a factory, and then use DI for the factory? – barto90 Feb 22 '14 at 08:44
  • For a complete worked example, see http://stackoverflow.com/questions/17168458/unity-autoregistration-convention-based-registration-unity/25654904#25654904. – Contango Sep 03 '14 at 23:04

2 Answers2

9

I took a closer look at RegisterTypes and it accepts a parameter of the type Func which you can use to provide the name for each instance. There was no need to create an extension to make this happen. After creating the attribute, it was quite easy to provide a specific name to each instance to be registered in the container.

First you create the attribute class:

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class UnityNamedInstanceAttribute : Attribute
{
    private readonly string instanceName;

    public UnityNamedInstanceAttribute(string instanceName)
    {
        this.instanceName = instanceName;
    }

    public string InstanceName
    {
        get { return this.instanceName; }
    }
}

Then set up a class to help with resolving the name of the instance:

public static class UnityNamedInstance
{
    public static string AttributeName(Type type)
    {
        var namedAttribute = type
            .GetCustomAttributes(typeof(UnityNamedInstanceAttribute), true)
            .FirstOrDefault()
            as UnityNamedInstanceAttribute;
        return namedAttribute != null ? namedAttribute.InstanceName : null;
    }

    public static string AttributeNameOrDefault(Type type)
    {
        return UnityNamedInstance.AttributeName(type) ?? WithName.Default(type);
    }
}

Declare your Test Classes using the attribute we created ealier:

public interface ITest
{
    void DebugName();
}

[UnityNamedInstance("Test A")]
public class TestA : ITest
{
    #region ITest Members

    public void DebugName()
    {
        Debug.WriteLine("This is TestA");
    }

    #endregion
}
[UnityNamedInstance("Test B")]
public class TestB : ITest
{
    #region ITest Members

    public void DebugName()
    {
        Debug.WriteLine("This is TestB");
    }

    #endregion
}

[UnityNamedInstance("Test C")]
public class TestC : ITest
{
    #region ITest Members

    public void DebugName()
    {
        Debug.WriteLine("This is TestC");
    }

    #endregion
}

And change your registration by convention method to:

        container.RegisterTypes(
            AllClasses.FromLoadedAssemblies().Where(t => t.Namespace == "DependencyInjectionExample.Test"),
            WithMappings.FromAllInterfaces, // This way you have the same instance for each interface the class implements
            UnityNamedInstance.AttributeNameOrDefault, // Use our helper to solve the name of the instance
            WithLifetime.ContainerControlled);

Or you could just avoid creating the attribute and the helper and name the instance after the class name, as follows:

        container.RegisterTypes(
            AllClasses.FromLoadedAssemblies().Where(t => t.Namespace == "DependencyInjectionExample.Test"),
            WithMappings.FromAllInterfaces,
            WithName.TypeName, // Use the type name for the instances
            WithLifetime.ContainerControlled);

Now you can access each class instance using the name passed to the attribute constructor in the class declaration or the class name:

        // Access each instance using the name you gave in the attribute
        container.Resolve<ITest>("Test A").DebugName();
        container.Resolve<ITest>("Test B").DebugName();
        container.Resolve<ITest>("Test C").DebugName();

And if you want to get all the registered instances that implements a specific interface:

        foreach (var test in container.Resolve<ITest[]>()) {
            test.DebugName();
        }

OR

        foreach (var test in container.ResolveAll<ITest>()) {
            test.DebugName();
        }

I'm very interested to know if this fits your needs. I appreciate your feedback!

Denis Brat
  • 487
  • 5
  • 14
0

Denis Brat's comment had the critical item in it that helped me solve a problem i had with this.

If you are using a Generic Repository with only constructors that take interfaces the GetInstance methods for the registered types fail with dependancy exceptions.

If you register the interfaces manually BEFORE you auto-register the concrete classes it works, i.e.

ViewModelLocator:

unityContainer.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>));

unityContainer.RegisterTypes(AllClasses.FromAssembliesInBasePath().Where(t => t.Namespace.StartsWith("Mynamespace.")),
            WithMappings.FromAllInterfaces,
            WithName.TypeName,
            WithLifetime.ContainerControlled,null,true);

Model:

public MainViewModel(IGenericRepository<Client> clientRepository, IExceptionManager exceptionManager )
    { whatever }

Views:

return ServiceLocator.Current.GetInstance<MainViewModel>("MainViewModel");

it will find them and return the correct instance.

Matma
  • 345
  • 4
  • 17