UnityContainer и внутренний конструктор - PullRequest
10 голосов
/ 20 июня 2011

У меня есть класс с внутренним конструктором, и я хочу разрешить его из Unity (2.0).

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

тогда я делаю

_container.Resolve<MyClass>();

, когда у меня так получаетсяисключение

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

IService зарегистрирован, и единственная проблема заключается в том, что конструктор является внутренним.Я действительно хочу, чтобы этот класс был общедоступным, но я хочу, чтобы его можно было создавать только через фабрику (в которой я на самом деле называю container.Resolve<MyClass>()).

Есть ли способ заставить Unity увидеть этот внутренний конструктор?Как InternalsVisibleTo или что-то?

Ответы [ 4 ]

9 голосов
/ 20 июня 2011

Я немного углубился в то, как вы можете расширить Unity для этой цели, и нашел некоторую интересную информацию.

Во-первых, кажется, что Unity выбирает, какой конструктор использовать, внутренним разрешением IConstructorSelectorPolicy.В Unity входит public abstract class ConstructorSelectorPolicyBase<TInjectionConstructorMarkerAttribute>, который включает в себя этот гем:

/// <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, и компания является private static методами в этом классе, которые в конечном итоге вызывают Type.GetConstructors (перегрузка без параметров, который возвращает только public конструкторы).Это говорит мне о том, что если вы можете организовать для Unity собственную политику выбора конструкторов, которая сможет выбирать любого конструктора, вы великолепны.

Есть хорошая документация о том, каксоздавать и использовать свои собственные контейнерные расширения, поэтому я полагаю, что вполне возможно сделать свой собственный CustomConstructorSelectorPolicy, который включает в себя соответствующие части DefaultUnityConstructorSelectorPolicy (который происходит от абстрактного базового класса и является значением по умолчанию, если только вызарегистрировать что-нибудь еще) и ConstructorSelectorPolicyBase (производное от этого напрямую, вероятно, не будет работать хорошо, потому что ключевые методы не virtual, но вы можете повторно использовать код).

Поэтому я 'Я бы сказал, что это выполнимо с небольшим количеством хлопот, но конечный результат будет довольно "чистым" с инженерной точки зрения.

6 голосов
/ 20 июня 2011

Unity будет смотреть только на общедоступные конструкторы, поэтому вам нужно сделать этот конструктор общедоступным.

Я действительно хочу, чтобы этот класс был общедоступным, но я хочу, чтобы его можно было создавать только через фабрику

В этом случае создайте фабрику:

public class MyClassFactory : IMyClassFactory
{
    private readonly IService service;

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

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

И зарегистрируйте:

_container.Register<IMyClassFactory, MyClassFactory>();

И разрешите:

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

Вытакже можно использовать Unity InjectionFactory:

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

. Чтобы это работало, сборка, содержащая этот код, должна видеть внутренние компоненты сборки, в которой хранится MyClass.Другими словами, сборка MyClass должна быть помечена InternalsVisibleTo.

. Будет также работать следующее:

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>());
})); 

Хотя вам не нужно делать конструктор общедоступнымэто отличный способ запутать ваш код: -)

2 голосов
/ 29 ноября 2011

Просто сделайте класс внутренним, а конструктор общедоступным ...

  1. Интерфейс общедоступный
  2. Класс внутренний
  3. Конструктор класса public.
0 голосов
/ 20 июня 2011

Возможно, что есть обходные пути / хаки, которые позволят вам сделать это с Unity 9, я не знаю, есть ли это, но в целом, если вы хотите, чтобы класс управлялся Unity (или любым контейнером IOC), ему нужны быть публичным с публичным конструктором.

Одним из вариантов может быть создание абстрактной фабрики, создающей класс с открытым конструктором, и сохранение конструктора класса внутренним. Недостатком является то, что вашей фабрикой будет управлять Unity, а сам ваш класс - нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...