Единица может быть сделана не бросить SynchronizationLockException все время?

Контейнер внедрения зависимости Единицы имеет то, что, кажется, широко известная проблема, где SynchronizedLifetimeManager будет часто вызывать Монитор. Метод выхода для броска SynchronizationLockException, который затем пойман и проигнорирован. Это - проблема для меня, потому что мне нравится отлаживать с набором Visual Studio для повреждения на любой вызванной исключительной ситуации, таким образом, каждый раз мое приложение запускает, я повреждаюсь на этом исключении многократно ни по какой причине.

Как я могу препятствовать тому, чтобы это исключение было брошено?

Везде, где это выходит, упоминается в другом месте в сети, совет обычно включает изменение настроек отладчика для игнорирования ее. Это сродни движению к доктору и высказыванию, "Доктор, Доктор, моя рука болит, когда я повышаю его", быть сказанным, "Ну, остановка повысить его". Я ищу решение, которое останавливает исключение, выданное во-первых.

Исключение происходит в методе SetValue, потому что это делает предположение, что GetValue назовут первым, где Монитор. Войдите назван. Однако классы LifetimeStrategy и UnityDefaultBehaviorExtension оба регулярно называют SetValue, не называя GetValue.

Я не должен изменить исходный код и поддержать мою собственную версию Единицы, таким образом, я надеюсь на решение, где я могу добавить некоторую комбинацию расширений, политик или стратегий к контейнеру, который гарантирует, что, если пожизненным менеджером является SynchronizedLifetimeManager, GetValue всегда называют перед чем-либо еще.

64
задан Rory MacLeod 30 June 2010 в 21:32
поделиться

3 ответа

Я уверен, что существует множество способов вызова SynchronizedLifetimeManager или его потомка ContainerControlledLifetimeManager, но было два сценария, которые вызывали у меня проблемы.

Первый был моей собственной ошибкой - я использовал инъекцию конструктора для предоставления ссылки на контейнер, и в этом конструкторе я также добавлял новый экземпляр класса в контейнер для будущего использования. Этот обратный подход привел к изменению менеджера времени жизни с Transient на ContainerControlled, так что объект, для которого Unity вызывала GetValue, не был тем же самым объектом, для которого она вызывала SetValue. Выученный урок: не делайте ничего во время сборки, что может изменить менеджер времени жизни объекта.

Второй сценарий заключался в том, что каждый раз при вызове RegisterInstance расширение UnityDefaultBehaviorExtension вызывает SetValue без предварительного вызова GetValue. К счастью, Unity является достаточно расширяемым, чтобы при достаточной кровожадности можно было обойти эту проблему.

Начните с нового расширения поведения, как это:

/// <summary>
/// Replaces <see cref="UnityDefaultBehaviorExtension"/> to eliminate 
/// <see cref="SynchronizationLockException"/> exceptions that would otherwise occur
/// when using <c>RegisterInstance</c>.
/// </summary>
public class UnitySafeBehaviorExtension : UnityDefaultBehaviorExtension
{
    /// <summary>
    /// Adds this extension's behavior to the container.
    /// </summary>
    protected override void Initialize()
    {
        Context.RegisteringInstance += PreRegisteringInstance;

        base.Initialize();
    }

    /// <summary>
    /// Handles the <see cref="ExtensionContext.RegisteringInstance"/> event by
    /// ensuring that, if the lifetime manager is a 
    /// <see cref="SynchronizedLifetimeManager"/> that its 
    /// <see cref="SynchronizedLifetimeManager.GetValue"/> method has been called.
    /// </summary>
    /// <param name="sender">The object responsible for raising the event.</param>
    /// <param name="e">A <see cref="RegisterInstanceEventArgs"/> containing the
    /// event's data.</param>
    private void PreRegisteringInstance(object sender, RegisterInstanceEventArgs e)
    {
        if (e.LifetimeManager is SynchronizedLifetimeManager)
        {
            e.LifetimeManager.GetValue();
        }
    }
}

Затем вам нужно найти способ заменить поведение по умолчанию. В Unity нет метода для удаления конкретного расширения, поэтому вам придется удалить все и снова вставить другие расширения:

public static IUnityContainer InstallCoreExtensions(this IUnityContainer container)
{
    container.RemoveAllExtensions();
    container.AddExtension(new UnityClearBuildPlanStrategies());
    container.AddExtension(new UnitySafeBehaviorExtension());

#pragma warning disable 612,618 // Marked as obsolete, but Unity still uses it internally.
    container.AddExtension(new InjectedMembers());
#pragma warning restore 612,618

    container.AddExtension(new UnityDefaultStrategiesExtension());

    return container;
}

Заметьте, что UnityClearBuildPlanStrategies? RemoveAllExtensions очищает все внутренние списки политик и стратегий контейнера, кроме одной, поэтому мне пришлось использовать другое расширение, чтобы избежать вставки дубликатов при восстановлении расширений по умолчанию:

/// <summary>
/// Implements a <see cref="UnityContainerExtension"/> that clears the list of 
/// build plan strategies held by the container.
/// </summary>
public class UnityClearBuildPlanStrategies : UnityContainerExtension
{
    protected override void Initialize()
    {
        Context.BuildPlanStrategies.Clear();
    }
}

Теперь вы можете смело использовать RegisterInstance, не боясь оказаться на грани безумия. Чтобы быть уверенным, вот несколько тестов:

[TestClass]
public class UnitySafeBehaviorExtensionTests : ITest
{
    private IUnityContainer Container;
    private List<Exception> FirstChanceExceptions;

    [TestInitialize]
    public void TestInitialize()
    {
        Container = new UnityContainer();
        FirstChanceExceptions = new List<Exception>();
        AppDomain.CurrentDomain.FirstChanceException += FirstChanceExceptionRaised;
    }

    [TestCleanup]
    public void TestCleanup()
    {
        AppDomain.CurrentDomain.FirstChanceException -= FirstChanceExceptionRaised;
    }

    private void FirstChanceExceptionRaised(object sender, FirstChanceExceptionEventArgs e)
    {
        FirstChanceExceptions.Add(e.Exception);
    }

    /// <summary>
    /// Tests that the default behavior of <c>UnityContainer</c> leads to a <c>SynchronizationLockException</c>
    /// being throw on <c>RegisterInstance</c>.
    /// </summary>
    [TestMethod]
    public void UnityDefaultBehaviorRaisesExceptionOnRegisterInstance()
    {
        Container.RegisterInstance<ITest>(this);

        Assert.AreEqual(1, FirstChanceExceptions.Count);
        Assert.IsInstanceOfType(FirstChanceExceptions[0], typeof(SynchronizationLockException));
    }

    /// <summary>
    /// Tests that <c>UnitySafeBehaviorExtension</c> protects against <c>SynchronizationLockException</c>s being
    /// thrown during calls to <c>RegisterInstance</c>.
    /// </summary>
    [TestMethod]
    public void SafeBehaviorPreventsExceptionOnRegisterInstance()
    {
        Container.RemoveAllExtensions();
        Container.AddExtension(new UnitySafeBehaviorExtension());
        Container.AddExtension(new InjectedMembers());
        Container.AddExtension(new UnityDefaultStrategiesExtension());

        Container.RegisterInstance<ITest>(this);

        Assert.AreEqual(0, FirstChanceExceptions.Count);
    }
}

public interface ITest { }
38
ответ дан 24 November 2019 в 16:01
поделиться

Это может вам помочь:

  • Перейдите в Отладка -> Исключения ...
  • Найдите исключения, которые действительно вас расстраивают, например SynchronizationLockException

Вуаля.

-8
ответ дан 24 November 2019 в 16:01
поделиться

К сожалению, ответ на ваш вопрос отрицательный. Я обсудил это с командой разработчиков здесь, в группе шаблонов и практик Microsoft (до недавнего времени я был руководителем разработчика), и у нас возникла ошибка, которую нужно учитывать в EntLib 5.0. Мы провели небольшое расследование и пришли к выводу, что это было вызвано неожиданными взаимодействиями между нашим кодом и отладчиком. Мы рассматривали исправление, но оно оказалось сложнее, чем существующий код. В конце концов, это стало приоритетом ниже других вещей и не дало планку на 5.

Извините, у меня нет лучшего ответа для вас. Если это хоть какое-то утешение, меня это тоже раздражает.

10
ответ дан 24 November 2019 в 16:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: