2011-04-11 2 views
11

Извинения за довольно неоднозначное название, но то, что я пытаюсь достичь, возможно, лучше указано в коде.C#: Элегантный способ обертывания вызовов методов

У меня есть клиент WCF. Когда я вызываю методы, я хотел бы объединить каждый вызов в некоторый код обработки ошибок. Таким образом, вместо того, чтобы непосредственно разоблачая методы, я создал следующую вспомогательную функцию на клиентском классе:

public T HandleServiceCall<T>(Func<IApplicationService, T> serviceMethod) 
    { 
     try 
     { 
      return serviceMethod(decorator); 
     } 
     [...] 
    } 

И код клиента использует его как это:

service.HandleServiceCall(channel => channel.Ping("Hello")); 

и призыв к Ping получает красиво завернутый в некоторую логику, которая будет пытаться обрабатывать любые ошибки.

Это отлично работает, за исключением того, что у меня теперь есть требование узнать, какие методы фактически вызываются в службе. Первоначально я надеялся просто осмотреть Func<IApplicationService, T>, используя деревья выражений, но не очень далеко.

Наконец, я остановился на шаблон Decorator:

public T HandleServiceCall<T>(Func<IApplicationService, T> serviceMethod) 
    { 
     var decorator = new ServiceCallDecorator(client.ServiceChannel); 
     try 
     { 
      return serviceMethod(decorator); 
     } 
     [...] 
     finally 
     { 
      if (decorator.PingWasCalled) 
      { 
       Console.Writeline("I know that Ping was called") 
      } 
     } 
    } 

И сам декоратор:

private class ServiceCallDecorator : IApplicationService 
    { 
     private readonly IApplicationService service; 

     public ServiceCallDecorator(IApplicationService service) 
     { 
      this.service = service; 
      this.PingWasCalled = new Nullable<bool>(); 
     } 

     public bool? PingWasCalled 
     { 
      get; 
      private set; 
     } 

     public ServiceResponse<bool> Ping(string message) 
     { 
      PingWasCalled = true; 
      return service.Ping(message); 
     } 
    } 

Это действительно неуклюжим и довольно много кода. Есть ли более элегантный способ сделать это?

+0

Где вы создаете декоратор? – smartcaveman

+3

Выражение деревьев должно быть путем. Можете ли вы показать код и сообщить нам, в чем проблема? –

+0

Звучит как работа для PostSharp. – geofftnz

ответ

2

Вы можете использовать выражение, а затем осмотреть тело.

Что-то вроде

public T HandleServiceCall<T>(Expression<Func<IApplicationService, T>> serviceMethod)  
{   
    try   
    {   
     var func = serviceMethod.Compile(); 
     string body = serviceMethod.Body.ToString(); 
     return func(new ConcreteAppService()); 
    }   
    catch(Exception ex) 
    { 
     ...  
       } 
} 
+1

Это, в сочетании с описанием Энрико о том, как проверить вызов метода, хорошо работает. Он также случайно решает другие проблемы, поскольку он не позволяет клиенту вводить более одного вызова метода - что является хорошим дополнением. – djskinner

+0

Мое окончательное решение - это комбинация ответов, предоставленных @Richard Friend и @Enrico Campidoglio, где я использовал код Энрико для идентификации вызываемого метода. – djskinner

2

Считаете ли вы использование аспектно-ориентированного подхода? Это похоже на то, что вам нужно.

Функциональность обертывания и другие функции «мета-метода» могут быть записаны как «ортогональные» аспекты того, что делают ваши методы serviceMethods.

Некоторые общие сведения о АОП: AOP in wikipedia

И потенциальное решение с контейнером: AOP with Windsor Castle

+0

Согласен, это хорошо подходит. Для меня это немного переборщило. Есть ли более простая, родная альтернатива? – djskinner

+0

более легкая версия может быть (предложена выше) PostSharp. Однако я его не использовал. – grzeg

1

Вот краткий пример использования дерева выражений:

public T HandleServiceCall<T>(Expression<Func<T>> serviceMethod) 
{ 
    try 
    { 
     return serviceMethod(); 
    } 
    finally 
    { 
     var serviceMethodInfo = ((MethodCallExpression)serviceMethod.Body).Method; 
     Console.WriteLine("The '{0}' service method was called", serviceMethodInfo.Name); 
    } 
} 

Обратите внимание, что этот пример предполагает, что выражение serviceMethod всегда содержит вызов метода.

Связанные ресурсы:

+0

И только один способ вызова? – djskinner

+0

Попытка проверить это, но Func не содержит определения для Body. – djskinner

+1

Вам нужно изменить тип аргумента метода 'HandleServiceCall ' на 'Expression >' –

0

Да, я считаю, что ваш код переваренные.

С точки зрения упаковки вашего кода для общего безопасного использования прокси, посмотрите here для приятной реализации. С его помощью легко:

using (var client = new Proxy().Wrap()) { 
client.BaseObject.SomeMethod(); 
} 

Теперь вы должны получить доступ к имени метода - для этого просто использовать Environment.StackTrace. Вам нужно добавить ходьбу в стек в Marc Gravell's Wrap.

+0

Этот подход немного отличается от того, как я разработал свой код. Если бы я сделал какой-то тяжелый рефакторинг, я бы определенно подумал об этом. – djskinner

Смежные вопросы