C #: элегантный способ обернуть вызовы метода - PullRequest
12 голосов
/ 11 апреля 2011

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

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

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

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

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

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

Это прекрасно работает, за исключением того, что теперь у меня есть требование знать, какие методы в действительности вызываются в службе. Сначала я надеялся просто проверить 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);
        }
    }

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

Ответы [ 4 ]

3 голосов
/ 11 апреля 2011

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

Что-то вроде

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)
    {
        ...     
              }
}
2 голосов
/ 11 апреля 2011

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

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

Общая информация об АОП: АОП в Википедии

И потенциальное решение с контейнером: AOP с Виндзорским замком

1 голос
/ 11 апреля 2011

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

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 голосов
/ 11 апреля 2011

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

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

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

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

Марка Гравелла
...