1

我已经实现了一个通用方法,用于在我的 Web 服务方法中调用函数并捕获异常。这个想法是集中异常处理并记录异常发生的原因:

public class WebServiceHandler
{
     public T Execute<T>(Func<T> body)
            {
                //wrap everything in common try/catch
                try
                {                
                    return body();
                }
                catch (SoapException)
                {
                    //rethrow any pre-generated SOAP faults
                    throw;
                }           
                catch (Exception ex)
                {

                    Logger.AddTextToLog(Logger.LogLevel.Error, "An error occured");
                    var innerExceptionMessage = ex.InnerException != null ? ex.InnerException.Message : "";
                    throw GenerateSoapException(
                                                ex.Message,
                                                innerExceptionMessage, 
                                                SoapException.ServerFaultCode);
                }
            }
}

我在我的网络服务方法中使用这样的方法:

[WebMethod]
GetXXX(string param1, string param2)
{
var request = new GetXXXRequest(param1, param2);
     return _webServiceHandler.Execute(() => _controller.GetXXX(request));
}

[WebMethod]
GetYYY(string param1)
{
var request = new GetYYYRequest(param1);
     return _webServiceHandler.Execute(() => _controller.GetYYY(request));
}

如果出现异常,我想在 Execute 方法中记录用作 controller.GetXXX(request) 和 controller.GetYYY(request) 方法的输入的参数名称和值。

我怎样才能做到这一点?还是有更好的方法来实现相同的目标?

4

5 回答 5

1

我没有尝试过表达,但我有代表的解决方案;

public static List<object> GetMethodParameterValues(Delegate method)
{
    var target = method.Target;
    if (target == null) return null;
    var fields = target.GetType().GetFields();
    var valueList = fields.Select(field => field.GetValue(target)).ToList();
    return valueList;
}
于 2014-07-18T05:30:56.427 回答
1

首先,您不能神奇地、隐式地查询运行时以获取该信息。

确实,您可以通过实例化StackTrace类或调用MethodBase.GetCurrentMethod()方法来隐式地了解当前正在执行的方法、其调用者、其调用者的调用者和所有堆栈跟踪(通用方法的特定通用参数除外)的完整身份.

生成的实例也确实包含MethodBase有关方法参数的信息,因此,您可能能够了解参数的名称,但这就是一切的结束。如果发明了一种允许您隐式探测参数或局部变量值的机制,那么一切都会慢很多

能做的,就是通过Expression<Lambda>班级及其随行人员帮助自己。它会有点慢,但是您可以选择是想要可审查且易于管理的代码,还是非常难以管理且非常快速的代码。

Expression<Lambda>是 LINQ 如何设法不对数据库表进行全表扫描,而是了解您对查询所做的操作并将其(在运行时)翻译成 SQL 或您可能想象的任何其他语言(取决于实际的 LINQ 提供程序)。

首先,我建议将关注点分为两类:

  1. 检索名称和值(尽可能隐式)
  2. 使用名称和值(在任何地方)

要做到这一点,您需要考虑一个可以保存第 1 点结果的实体。在我对您的建议中,这将是一种Dictionary<string, object>但您可以做任何最适合您的事情。

我的建议可以这样使用:

public void SomeMethod(string x, int y) {

    IDictionary<string, object> paramValues = Helper.TapInto(
        () => x, 
        () => y
    );

    // paramValues["x"] an paramValues["y"] will hold the values of x and y

}

所以,关于编码位。你可以这样写一个Helper类:

public static class Helper {
}

在那个Helper类中,你可以发明一个静态方法(我叫我TapInto的,也许这不是最好的名字),它接收一个原始的Expression<Func<object>>实例数组。它使用params修饰符来做到这一点,以便您可以轻松地将隐式 lambda 传递给它。作为回报,它为您提供了一个哈希表stringobject代表“反编译”的变量名称及其相关值。

就我而言,我还创建了同一方法的私有重载,它实际上是一个“扩展”方法,以使代码更清晰。

public static class Helper {

    // ... an overload of the TapInto method is about to appear right here

    public static IDictionary<string, object> TapInto(params Expression<Func<object>>[] parameterTouchers) {
        var result = new Dictionary<string, object>();

        foreach (var toucher in parameterTouchers) {
            string name;
            object value;
            toucher.TapInto(out name, out value);
            result[name] = value;
        }

        return result;

    }

所以所有公共方法所做的就是遍历列表并将产生的结果累积到字典中。

接下来让我们看看真正的魔法,它发生在toucher.TapInto(out name, out value)调用中:

public static class Helper {

    private static void TapInto(this Expression<Func<object>> @this, out string name, out object value) {

        Expression expression = @this.Body;
        if (expression is UnaryExpression)
            expression = (expression as UnaryExpression).Operand;

        name = (expression as MemberExpression).Member.Name;

        Func<object> compiledLambda = @this.Compile();
        value = compiledLambda();

    }

    // ... public helper method right here
}

我们在这里所做的是“我们正在用放大镜观察”lambda。因为我们要使用object变量以外的东西,所以很快就会观察到隐式转换,比如

.. int someParameter ..
object obj = someParameter;

这仅隐含在实际的 C# 代码中,但实际上被编译为显式转换:

object obj = (object)someParameter;

但是你可能有一个简单的object参数,比如object anotherParam,在这种情况下根本不会有任何转换。

这就是为什么在观察表达式的复杂细节时,我认为我可能会找到转换(由UnaryExpressionclass 表示)或没有。

实际上就像是说:在这种特殊情况下,我与调用代码的约定是它可能只向我发送属于以下两类的内容:

  1. 立即object变量引用:() => someObjectVariable
  2. 带转换的变量引用:() => (object)x

合约还意外声明“转换”位可以用 替换UnaryExpression,例如:() => !someBool.

它还指出您不能执行以下操作:

  • () => 123
  • 或者() => a + b + c + 100
  • 或这些方向的其他任何东西

所以,总结一下:

  1. 你可以写你的好小帮手
  2. 你可以在任何你想使用它的地方使用它来生成参数名称和它们的值之间的映射,尽管它不是 100% 隐式的,但是如果你重命名一个参数而不完全重构它至少不会编译,或者它会让你重命名参数如果您选择使用重构重命名参数的引用(它也适用于字段、局部变量等)
  3. 在对它们感兴趣的代码部分之间传递您的字典并相应地使用它们!
于 2013-10-14T10:48:57.930 回答
0

您可以使用 Execute 方法的另一个参数从函数中获取回调。操作或您的结果类型。在这种情况下,例如字符串:

public T Execute<T>(Func<T> body, Action<string> callback)
            {
                //wrap everything in common try/catch
                try
                {  
                     //do stuff              
                    callback("results are...");
                }

您还可以使用委托:

public void CallbackDelegate( string str ); 


 public T Execute<T>(Func<T> body, CallbackDelegate callback)
            {
                //wrap everything in common try/catch
                try
                {  
                     //do stuff              
                    callback("results are...");
                }
于 2013-10-14T10:08:58.233 回答
0

我最终做的是将请求类作为参数添加到 Execute 方法,如下所示:

public T Execute<T, TU>(Func<T> body, TU parameterClass) where TU : class
{
 //wrap everything in common try/catch
        try
        {                
            return body();
        }
        catch (SoapException)
        {
            //rethrow any pre-generated SOAP faults
            throw;
        }
        catch (Exception ex)
        {
            var serializedObject = ParameterUtil.GetPropertyNamesAndValues(parameterClass);                
            Logger.AddTextToLog(Logger.LogLevel.Error, string.Format("An error occured when calling braArkiv Web Services. Web service method arguments: {0}", serializedObject), ex);
            var innerExceptionMessage = ex.InnerException != null ? ex.InnerException.Message : "";
            throw GenerateSoapException(
                                        ex.Message,
                                        innerExceptionMessage,
                                        SoapException.ServerFaultCode);
        }
}


 public static class ParameterUtil
    {       
        public static string GetPropertyNamesAndValues<T>(T o) where T : class
        {
            using (var stringWriter = new StringWriter())
            {
                var xmlSerializer = new XmlSerializer(o.GetType());
                xmlSerializer.Serialize(stringWriter, o);
                stringWriter.Close();
                return stringWriter.ToString();
            }            
        }        
    }

用法:

[WebMethod]
GetXXX(string param1, string param2)
{
var request = new GetXXXRequest(param1, param2);
     return _webServiceHandler.Execute(() => _controller.GetXXX(request), request);
}

当发生异常时,我只需序列化包含 Web 服务方法参数的 parameterClass 参数并将字符串添加到我的日志中。

感谢大家对我的问题的建设性意见!

于 2013-10-16T09:19:50.250 回答
0

也许是这样的:

sealed class Param
{
    public string Name
    {
        get;
        private set;
    }

    public object Value
    {
        get;
        private set;
    }

    public Param(string name, object value)
    {
        Name = name;
        Value = value;
    }
}

public T Execute<T>(Func<T> body, params Param[] parameters)
{
    //wrap everything in common try/catch
    try
    {
        return body();
    }
    catch (SoapException)
    {
        //rethrow any pre-generated SOAP faults
        throw;
    }
    catch (Exception ex)
    {

        Logger.AddTextToLog(Logger.LogLevel.Error, "An error occured");

        foreach (var parameter in parameters)
        {
            Logger.AddTextToLog(
                                Logger.LogLevel.Error,
                                string.Format(
                                              "{0} : {1}",
                                              parameter.Name,
                                              parameter.Value ?? "null"));
        }

        var innerExceptionMessage = ex.InnerException != null ? ex.InnerException.Message : "";
        throw GenerateSoapException(
                                    ex.Message,
                                    innerExceptionMessage,
                                    SoapException.ServerFaultCode);
    }
}

然后你打电话:

[WebMethod]
GetXXX(string param1, string param2)
{
    var request = new GetXXXRequest(param1, param2);
    return _webServiceHandler.Execute(() => _controller.GetXXX(request)
                                      new Parameter("param1", param1),
                                      new Parameter("param2", param2));
}
于 2013-10-14T10:22:24.460 回答