Unity BuildUp не работает для синглтона - PullRequest
1 голос
/ 13 мая 2011

У меня возникла следующая проблема с платформой Unity.

У нас есть одноэлементные классы в нашем проекте.У них есть некоторые свойства, которые должны вводиться контейнером Unity.Вот код:

private static SomeClass m_Instance;

private SomeClass()
{ }

public static SomeClass Instance
{
    get
    {
        if (m_Instance == null)
        {
            lock (typeof(SomeClass))
            {
                if (m_Instance == null)
                {
                    IUnityContainer container = ContainerAccessor.GetContainer();
                    m_Instance = container.BuildUp<SomeClass>(new SomeClass());
                }
            }
        }

        return m_Instance;
    }
}

Этот код не выполняется со следующим исключением: Тип SomeClass не может быть создан.Вы должны сконфигурировать контейнер для предоставления этого значения.

Я покопался в коде Unity и обнаружил, что проблема была вызвана методом PreBuildUp, который вызывает GuardTypeIsNonPrimitive, оба определены в Microsoft.Practices.ObjectBuilder2Класс .DynamicMethodConstructorStrategy.Вот фрагмент его кода:

public override void PreBuildUp(IBuilderContext context)
{
    ...
    SelectedConstructor selectedConstructor = context.Policies.Get<IConstructorSelectorPolicy>(context.BuildKey, out list).SelectConstructor(context, list);
    GuardTypeIsNonPrimitive(context, selectedConstructor);
    ...
}

private static void GuardTypeIsNonPrimitive(IBuilderContext context, SelectedConstructor selectedConstructor)
{
    Type type = context.BuildKey.Type;
    if (!type.IsInterface && ((type == typeof(string)) || (selectedConstructor == null)))
    {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.TypeIsNotConstructable, new object[] { type.Name }));
    }
}

Как мы видим, Unity пытается найти конструктор для класса, который должен быть собран.Поскольку единственный конструктор, определенный для SomeClass, является закрытым, Unity ничего не находит и передает null в GuardTypeIsNonPrimitive.И этот метод вызывает исключение.На данный момент я определил публичный конструктор для SomeClass (просто чтобы доказать концепцию), все работало нормально.

Вопросы:

  1. ОБНОВЛЕНИЕ: Почему метод BuildUp требует определения конструктора?

  2. Есть идеи, как это обойти?(Удалить синглтон не вариант)

Ответы [ 2 ]

2 голосов
/ 13 мая 2011

Как насчет использования ContainerControlledLifetimeManager (для одного контейнера контейнера) для вашего SomeClass.Он не будет одиночным как есть, но контейнер всегда будет возвращать один и тот же экземпляр

Вместо SomeClass.Instance вы будете звонить container.Resolve<SomeClass>()

1 голос
/ 01 июня 2013

Никогда не поздно узнать что-то новое, и спустя более двух лет я готов ответить на свой вопрос.

Короче говоря: это известная ошибка , и она была исправлена ​​в 2.1.505.2. Подробности ниже.

Об ошибке сообщалось в сентябре 2010 года для Unity 2.0, и она оставалась в фреймворке до выпуска версии 2.1.505.2 в августе 2012 года. Это объясняет, почему мы столкнулись с ней, но не почему мы не смогли отследить отчет об ошибках в Google ...

Вот код, необходимый для воспроизведения проблемы.

Определение одноэлементного класса (обратите внимание, что класс вообще не содержит никаких зависимостей):

public class SingletonClass
{
    private static SingletonClass m_Instance;

    private SingletonClass()
    {
    }

    public static SingletonClass Instance
    {
        get
        {
            if (m_Instance == null)
            {
                m_Instance = new SingletonClass();
            }

            return m_Instance;
        }
    }
}

Фактический BuildUp Звоните:

UnityContainer container = new UnityContainer();
SingletonClass singleton = container.BuildUp(SingletonClass.Instance);

До версии 2.1.505.0 этот код выдавал либо InvalidOperationException, либо ResolutionFailedException. Начиная с версии 2.1.505.2, этот код работает нормально (с моей точки зрения, как и положено).

Интересно знать, что фактическое исправление было сделано путем переписывания фрагмента кода, который я обрисовал в общих чертах в вопросе. Вот как выглядят соответствующие части Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy:

public override void PreBuildUp(IBuilderContext context)
{
    ...
    SelectedConstructor selectedCtor = selector.SelectConstructor(context, resolverPolicyDestination);

    GuardTypeIsNonPrimitive(context);
    ...
}

private static void GuardTypeIsNonPrimitive(IBuilderContext context)
{
    var typeToBuild = context.BuildKey.Type;
    if (!typeToBuild.GetTypeInfo().IsInterface)
    {
        if (typeToBuild == typeof(string))
        {
            throw new InvalidOperationException(
                string.Format(
                    CultureInfo.CurrentCulture,
                    Resources.TypeIsNotConstructable,
                    typeToBuild.GetTypeInfo().Name));
        }
    }
}

Самая важная часть здесь заключается в том, что теперь сторожевой метод GuardTypeIsNonPrimitive не учитывает конструктор - только сам тип. Я думаю, что это было корнем проблемы.

Тот факт, что это ошибка, отвечает на первый вопрос в посте. Как насчет второго? Как обойти эту проблему? Если вы используете Unity 2.1.505.2 и выше, у вас нет этой проблемы, поэтому наиболее предпочтительным вариантом является обновление вашей версии Unity. Однако, если вам приходится иметь дело с 2.1.505.0 и ниже - есть несколько подходов:

  1. Рефакторинг кода, чтобы избавиться от синглтона, и зарегистрировать тип в контейнере с соответствующим временем жизни, как это предложено Ladislav Mrnka в другом ответе на этот вопрос. В нашем случае это было невозможно, но все же иногда это может быть путь.

  2. Загрузите исходные коды и перекомпилируйте их, изменив реализацию GuardTypeIsNonPrimitive с версией, опубликованной выше.

  3. Реализуйте собственную инъекцию, например, как метод расширения класса UnityContainer. Один пример можно найти здесь (сама ссылка устарела, поэтому вместо ссылки на версию веб-архива).

Как всегда правильный выбор зависит от конкретной ситуации.

...