Unity: зарегистрируйте два интерфейса как один синглтон с перехватом - PullRequest
6 голосов
/ 20 марта 2011

У меня есть класс, который реализует два интерфейса, и я хочу применить перехват к методам класса.

Я следую совету в Unity Зарегистрируйте два интерфейса как один синглтон , но результаты меня удивляют. В двух словах, кажется, что мой CallHandler вызывается дважды. Самый короткий пример, который у меня есть, это:

public interface I1
{
    void Method1();
}

public interface I2
{
    void Method2();
}

public class C : I1, I2
{
    [Log]
    public void Method1() {}

    public void Method2() {}
}

public class LogAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new LogCallHandler();
    }
}

public class LogCallHandler : ICallHandler
{
    public IMethodReturn Invoke(
        IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        Console.WriteLine("Entering " + input.MethodBase.Name);
        var methodReturn = getNext().Invoke(input, getNext);
        Console.WriteLine("Leaving " + input.MethodBase.Name);
        return methodReturn;
    }

    public int Order { get; set; }
} 

void Test()
{
    IUnityContainer container = new UnityContainer();
    container.AddNewExtension<Interception>();
    container.RegisterType<C>(new ContainerControlledLifetimeManager());

    container.RegisterType<I1, C>(
        new Interceptor<TransparentProxyInterceptor>(),
        new InterceptionBehavior<PolicyInjectionBehavior>());

    container.RegisterType<I2, C>(
        new Interceptor<TransparentProxyInterceptor>(),
        new InterceptionBehavior<PolicyInjectionBehavior>());

    container.Resolve<I1>().Method1();
}

Что дает этот вывод:

Entering Method1
Entering Method1
Leaving Method1
Leaving Method1

Удаление строки «container.RegisterType I2, C» приводит к тому, что журнал появляется только один раз. Добавление третьего интерфейса, I3, аналогичного I2, приводит к тому, что журнал появляется три раза.

Я бы ожидал, что Журнал будет вызван только один раз. Вероятно, я могу добиться этого, если LogCallHandler определит, вызывается ли он из другого LogCallHandler, но это выглядит не элегантно.

Первоначально я хотел применить поведение перехвата к C, а не к I1 и I2 по отдельности, но для этого требуется, чтобы C наследовал от MarshalByRefObject, что является ограничением, которое я пока не готов навязывать.

Есть ли альтернативный способ?

Ответы [ 2 ]

5 голосов
/ 20 марта 2011

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

Я запустил эту конфигурацию в тестовом консольном проекте и получил желаемый результат. Престижность за включение рабочего фрагмента, который изолировал вашу проблему!

var container = new UnityContainer()
    .AddNewExtension<Interception>()
    .RegisterType<I1, C>()
    .RegisterType<I2, C>()
    .RegisterType<C>(
        new ContainerControlledLifetimeManager(),
        new Interceptor<TransparentProxyInterceptor>(),
        new InterceptionBehavior<PolicyInjectionBehavior>()
    );
4 голосов
/ 21 марта 2011

Как оказалось, небольшая модификация моего исходного фрагмента дает решение:

public interface I1
{
    void Method1();
}

public interface I2
{
    void Method2();
}

public class C : I1, I2
{
    public int Data = 0;

    [Log]
    public void Method1() { Console.WriteLine("Method1 " + Data); Data = 1; }

    [Log]
    public void Method2() { Console.WriteLine("Method2 " + Data); Data = 2; }
}

public class LogAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new LogCallHandler();
    }
}

public class LogCallHandler : ICallHandler
{
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        Console.WriteLine("Entering " + input.MethodBase.Name);
        var methodReturn = getNext().Invoke(input, getNext);
        Console.WriteLine("Leaving " + input.MethodBase.Name);
        return methodReturn;
    }

    public int Order { get; set; }
} 

void Test()
{
    IUnityContainer container = new UnityContainer();
    container.AddNewExtension<Interception>();
    container.RegisterType<C>(new ContainerControlledLifetimeManager());

    container.RegisterType<I1, C>();
    container.RegisterType<I2, C>();
    container.Configure<Interception>().SetInterceptorFor<I1>(new TransparentProxyInterceptor());
    container.Configure<Interception>().SetInterceptorFor<I2>(new TransparentProxyInterceptor());

    container.Resolve<I1>().Method1();
    container.Resolve<I2>().Method2();
    container.Resolve<C>().Method2();
}

Единственное отличие состоит в том, что я устанавливаю перехват для I1 и I2 вне вызова RegisterType.Выходные данные выше:

Entering Method1
Method1 0
Leaving Method1
Entering Method2
Method2 1
Leaving Method2
Method2 2

Что дает мне следующее:

  • Я перехватываю реализацию C как I1, так и I2.
  • Есть толькоодин экземпляр C (о чем свидетельствует изменение члена Data).
  • Я могу добраться до одного экземпляра C (т. е. container.Resolve<C>() работает, хотя и без перехвата).

Мой пример использования здесь - модульные тесты: мой код полагается на перехват некоторых его функций (в частности, управление транзакциями).C - это фиктивный объект, который я предоставляю своему тестируемому классу, и он реализует два интерфейса, которые нужны тестируемому классу.Когда запускается модульное тестирование, я хочу получить доступ к фиктивному объекту для проверки содержимого на нем.

Недостатком вышеупомянутого решения является то, что я могу применять только PolicyInjectionBehavior здесь (на самом деле код выше неуказать любое InterceptionBehavior: при использовании container.Configure<Interception>, PolicyInjectionBehavior автоматически используется Unity, насколько я могу судить. Для моего случая использования это приемлемо.

Я должен признать, что я удивлен, чтоНастройка перехвата во время RegisterType дает результаты, отличные от настройки по отдельности. Если кто-то может объяснить это, я был бы рад понять, почему это так.

...