Logging is easy. We’ve all been there and done that, but when how do you log a request/response object of a webservice, specially when the WSDLs are being changed constantly? The requirements were easy, just log all the property names along with their values. The first obvious approach is the good old fashioned style:

1
2
3
4
5
6
7
8
9
public static string GetRequestLog(this ServiceRequest request)
{
var sb = new StringBuilder();
sb.AppendFormat("FirstProperty:{0}", request.FirstProperty);
sb.AppendFormat("SecondProperty:{0}", request.FirstProperty);
...
sb.AppendFormat("LastProperty:{0}", request.LastProperty);
return sb.ToString();
}

The problem is you code includes name and value of the property in two separate places and when you get an updated WSDL with most of the property names changed, you have to update two places, one of them being the property name in a string which makes it just harder to refactor and rename, but what can you do? You can shrug and say so be it, that is the problem at hand, until the next day you’ll be handled the newer version of the WSDL, or you can do something about it.

If performance is not an issue (well you should know better to turn the debugging on in production), you can get away by passing in an expression of the property. This was you’re passing in the property as well as it’s value!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public static string GetRequestLog(this ServiceRequest request) 
{
var sb = new StringBuilder();
sb.AppendProperty(() => request.FirstProperty);
sb.AppendProperty(() => request.SecondProperty);
...
sb.AppendProperty(() => request.HundredthProperty);
return sb.ToString();
}

public static class StringBuilderExtension
{
public static void AppendProperty<T>(this StringBuilder sb, Expression<Func<T>> property)
{
var memberInfo = GetMemberInfo(property);
sb.AppendFormat("{0}: {1}", memberInfo.Name, SafeToString(property.Compile().Invoke()));
}

private static MemberInfo GetMemberInfo(Expression expression)
{
var lambda = (LambdaExpression)expression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)lambda.Body;
}
return memberExpression.Member;
}

private static string SafeToString(object obj)
{
return obj == null ? ("null") : obj.ToString();
}
}

This of course is no magic these days, you probably have seen this when raising NotifyPropertyChange event in Caliburn or reading my blog post back in September 2008.