Backstory: Throughout our project we constantly find ourselves using the System.ComponentModel.DataAnnotations.Validator
object as well as attributes to validate properties passed in to our API. The Validator
object requires passing in the property name as well as the value, which is fair, but we are all a little grossed out by the [constantly-increasing] number of magic strings being passed around for these property names. Not only this entails the risk of mistyping the property name, it also reduces maintainability in the case that one of those properties get renamed (again, there are way too many of them).
To automatically resolve the member name, I created this helper method (for the sake of simplicity, let's assume I'm only dealing with reference-type properties, hence the P: class
constraint):
public static P ValidateProperty<T, P>(T obj, Expression<Func<T, P>> member, List<ValidationResult> results, Func<P, P> onValidCallback = null)
where T : class
where P : class
{
var expr = member.Compile();
var memberName = ((MemberExpression)member.Body).Member.Name;
var value = expr(obj);
bool isValid = Validator.TryValidateProperty(value, new ValidationContext(obj) { MemberName = memberName }, results);
return isValid ? (onValidCallback != null ? onValidCallback(value) : value) : null;
}
And I simply call it like this:
var value = ValidateProperty(myObject, x => x.PropertyFoo, errors, result => result.Trim());
Which works like a charm and doesn't involve any magic strings being passed around.
A sample request would look like this:
public class Request
{
public class C1
{
Property1;
Property2;
...
}
public class C2
{
Property1;
Property2;
...
}
public class C3
{
Property1;
Property2;
...
}
...
}
My concern here however, is on performance implications for member.Compile()
. Since there are many different possible permutations of T, P
(C1, Property1
, C1, Property2
, C2, Property1
etc.) I won't be able to cache the compiled expression and execute it on the next call, unless both T
and P
are of the same type, which happens rarely.
I could optimize this by changing Expression<Func<T, P>> member
to Expression<Func<T, object>> member
so that now I would have to only cache the expression once per type of T
(i.e. C1
, C2
, etc.)
I was wondering if anyone has any input on a better way to do it, or am I trying to "over engineer" the problem? Is there a common pattern for situations that involve passing around magic strings over and over again?
Another option: Rather than using C# 6 features or compiling, just use reflection. Now you're trading compile performance degradation for reflection performance degradation (which is probably less).
...
var property = (PropertyInfo) ((MemberExpression)member.Body).Member;
var propertyName = property.Name;
var value = property.GetValue(obj, new object[0]);
...