UnityContainer and internal constructor
Asked Answered
L

4

12

I have a class with internal constructor and want to Resolve it from Unity (2.0).

public class MyClass {
    internal MyClass(IService service) {
    }
}

then I'm doing

_container.Resolve<MyClass>();

when I do so I have an exception

Exception is: InvalidOperationException - The type MyClass cannot be constructed. 

IService is registered and the only problem is that constructor is internal. I really want this class to be public, but I want it to be creatable only via a factory (in which I'm actually calling container.Resolve<MyClass>()).

Is there a way to make Unity see that internal constructor? Like InternalsVisibleTo or something?

Laager answered 20/6, 2011 at 13:48 Comment(1)
Why don't you want to make this ctor public? Are you creating a reusable library?Obcordate
Q
10

I dug a little into how you might extend Unity for this purpose, and found some interesting information.

First, it seems that Unity selects which constructor to use by internally resolving an IConstructorSelectorPolicy. Included in Unity is the public abstract class ConstructorSelectorPolicyBase<TInjectionConstructorMarkerAttribute>, which includes this gem:

/// <summary>
/// Choose the constructor to call for the given type.
/// </summary>
/// <param name="context">Current build context</param>
/// <param name="resolverPolicyDestination">The <see cref='IPolicyList'/> to add any
/// generated resolver objects into.</param>
/// <returns>The chosen constructor.</returns>
public SelectedConstructor SelectConstructor(IBuilderContext context, IPolicyList resolverPolicyDestination)
{
    Type typeToConstruct = context.BuildKey.Type;
    ConstructorInfo ctor = FindInjectionConstructor(typeToConstruct) ?? FindLongestConstructor(typeToConstruct);
    if (ctor != null)
    {
        return CreateSelectedConstructor(context, resolverPolicyDestination, ctor);
    }
    return null;
}

FindInjectionConstructor and company are private static methods in this class which ultimately end up calling Type.GetConstructors (the overload with no parameters, which only returns public constructors). This tells me that if you can arrange for Unity to use your own constructor selector policy, which would be able to select any constructor, you are golden.

There is good documentation about how to make and utilize your own container extensions, so I imagine it's quite possible to make your own CustomConstructorSelectorPolicy that includes the relevant portions of DefaultUnityConstructorSelectorPolicy (which derives from the abstract base class and is the default unless you register something else) and ConstructorSelectorPolicyBase (deriving from this directly would probably not work well because key methods are not virtual, but you can reuse the code).

Therefore I 'd say it's doable with a moderate amount of hassle, but the end result would be quite "pure" from an engineering point of view.

Quadrinomial answered 20/6, 2011 at 14:30 Comment(1)
Here is an example of usage of custom IConstructorSelectorPolicy msdn.microsoft.com/en-us/library/dn178464(v=pandp.30).aspxMinstrelsy
O
7

Unity will only look at public constructors, so you need to make this constructor public.

I really want this class to be public, but I want it to be creatable only via a factory

In that case, create a factory:

public class MyClassFactory : IMyClassFactory
{
    private readonly IService service;

    public MyClassFactory(IService service)
    {
        this.service = service;
    }

    MyClass IMyClassFactory.CreateNew()
    {
        return new MyClass(this.service);
    }
}

And register:

_container.Register<IMyClassFactory, MyClassFactory>();

And resolve:

_container.Resolve<IMyClassFactory>().CreateNew();

You can also use Unity's InjectionFactory:

container.Register<MyClass>(new InjectionFactory(c => 
{   
    return new MyClass(c.Resolve<IService>());
})); 

For this to work the assembly that holds this code should be able to see the internals of the assembly that holds the MyClass. In other words the MyClass assembly should be marked with InternalsVisibleTo.

What would also work is the following:

public static class MyClassFactory
{
    public static MyClass CreateNew(IService service)
    {
        return new MyClass(service);
    }
}

container.Register<MyClass>(new InjectionFactory(c => 
{   
    return MyClassFactory.Create(c.Resolve<IService>());
})); 

Although you won't have to make the constructor public, it is a great way to obfuscate your code :-)

Obcordate answered 20/6, 2011 at 14:17 Comment(1)
Thanks, that's almost exactly what I'm doing :) I just don't like to change that factory every time MyClass dependencies will change. So, searching for a way to do this directly via Unity somehowLaager
I
3

Just make the class internal and the constructor public...

  1. Interface public
  2. Class internal
  3. Constructor of class public.
Injun answered 29/11, 2011 at 16:12 Comment(0)
G
0

It's possible there are workarounds/hacks that would allow you to do this with Unity 9I don't know if any), but in general if you want a class to be managed by Unity (or any IOC container), it needs to be public with a public constructor.

One option might be to make an abstract factory that creates the class that has a public constructor, and keep the class's constructor internal. The downside is then your factory will be managed by Unity, but your class itself will not.

Gerrigerrie answered 20/6, 2011 at 14:15 Comment(1)
"it needs to be public with a public constructor." - that is wrong, Unity works perfectly with internal classes with public constructors. That is the case when I want to manage my internal dependencies with IoC. What I've done is an abstract factory. And within it I want to create an instance of MyClass. I can do new MyClass(_container.Resolve<IService>()), but don't want to do it manually.Laager

© 2022 - 2024 — McMap. All rights reserved.