我已经实现了一种泛型方法,用于在我的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)方法的输入的参数名称和值。
我怎样才能做到这一点? 或者有更好的方法来实现相同的目标吗?
答案 0 :(得分:1)
首先,你无法神奇地,隐式地在运行时查询该信息。
通过实例化{{1>,您可以隐式学习当前正在执行的方法,调用者,调用者的调用者和所有堆栈跟踪的完整标识(通用方法除特殊通用参数外) }}或通过调用StackTrace
方法。
生成的MethodBase.GetCurrentMethod()
实例也保存有关方法参数的信息,因此,您可能能够学习参数的名称,但这就是它的全部结束。如果一个允许你隐式探测参数或局部变量值的机制就会被发明,那么一切将慢很多。
你可以做什么,通过MethodBase
班级及其随行人员来帮助自己。它会有点慢,但你可以选择是否需要可审查和易于管理的代码,或者是难以管理和非常快速的代码。
Expression<Lambda>
是LINQ如何管理不对数据库表进行全表扫描,而是理解您对查询所执行的操作并将其翻译(在运行时)到SQL或您可能想到的任何其他语言(取决于实际的LINQ提供程序)。
首先,我建议将问题分为两类:
为了实现这一点,你需要考虑一个可以保持第1点结果的实体。在我给你的建议中,这将是Expression<Lambda>
的一种,但你可以做任何最适合你的事。
我的建议可以这样使用:
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>>
修饰符执行此操作,以便您可以轻松地将隐式lambda传递给它。作为返回,它为您提供从params
到string
的哈希表,表示“反编译”变量名称及其关联值。
就我而言,我还创建了同一方法的私有重载,实际上是一种“扩展”方法,以使代码更清晰。
object
因此,所有公共方法都会遍历列表并将生成的结果累积到字典中。
接下来让我们看看真正的魔术,它发生在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)
我们在这里做的是“我们正在用放大镜查看lambda”。
因为我们要使用除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
}
变量之外的其他东西,所以很快就会发现像
object
仅隐含在实际的C#代码中,但实际上是作为显式转换编译的:
.. int someParameter ..
object obj = someParameter;
但您可能有一个简单的object obj = (object)someParameter;
参数,例如object
,在这种情况下根本就没有转化。
这就是为什么在观察表达式错综复杂的细节时,我认为我可能会找到一个转换(由object anotherParam
类表示)。
实际上它就像是说:在这种特殊情况下,我与调用代码的契约是可能只向我发送属于这两类的东西:
UnaryExpression
变量引用:object
() => someObjectVariable
合同还偶然声明“转化”位可以替换为() => (object)x
,例如:UnaryExpression
。
它还声明你不能做类似的事情:
() => !someBool
() => 123
所以,把它包起来:
答案 1 :(得分: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;
}
答案 2 :(得分: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...");
}
答案 3 :(得分: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));
}
答案 4 :(得分: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参数,并将该字符串添加到我的日志中。
谢谢大家对我的问题的建设性意见!