15

为相当模棱两可的标题道歉,但我想要实现的目标可能在代码中更好地说明。

我有一个 WCF 客户端。当我调用方法时,我想将每个调用包装在一些错误处理代码中。因此,我没有直接公开方法,而是在客户端类上创建了以下帮助函数:

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

客户端代码像这样使用它:

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

并且对 Ping 的调用很好地包含在一些尝试处理任何错误的逻辑中。

这很好用,只是我现在需要知道服务上实际调用了哪些方法。最初,我希望只检查Func<IApplicationService, T>使用表达式树,但没有走得太远。

最后,我选择了一个装饰器模式:

    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

4 回答 4

3

您可以使用表达式,然后检查正文。

就像是

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)
    {
        ...     
              }
}
于 2011-04-11T10:49:30.880 回答
2

您是否考虑过使用面向方面的方法?这听起来正是您所需要的。

包装异常和其他“元方法”功能可以写成与您的 serviceMethods 所做的“正交”的方面。

关于 AOP 的一些一般信息:维基百科中的 AOP

还有一个容器的潜在解决方案:温莎城堡的 AOP

于 2011-04-11T10:36:23.190 回答
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表达式始终包含方法调用。

相关资源:

于 2011-04-11T10:45:57.237 回答
0

是的,我相信你的代码煮过头了。

在包装代码以实现常见的安全处理代理方面,请在此处查找一个不错的实现。使用它很容易:

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

现在您还需要访问方法名称 - 为此只需使用Environment.StackTrace. 您需要在 Marc Gravell 的Wrap.

于 2011-04-11T11:06:01.517 回答