I wanted to come up with a way of establishing the type name and property name when applying general validations within my code. So, for example for this validation:.
var bool = Requires.IsNotNull(user.lastName)
I have to explicitly code the exception:
throw NullReferenceException("User.lastName")
What I’d like is for all this to happen when I declare the test, without double typing the type and property name.
This is still very much a work in progress but I’ve got the basics of what I’m trying to achieve by using the Expression
class.
Here’s how it’s used:
[Test]
public void FirstTest()
{
var user = new User()
{
Name = "John",
Age = 32,
Skills = new List<string> { "digging", "mowing", "pruning" }
};
var decorator = new NaiveExpressionDecorator<User>(x => x.Name != null);
decorator.propertyName.Should().Be("User.Name");
}
And here’s the basic implementation:
public sealed class NaiveExpressionDecorator<T>
{
private readonly Expression<Func<T, bool>> rule;
private readonly Func<T, bool> compiled;
public NaiveExpressionDecorator(Expression<Func<T, bool>> rule)
{
this.rule = rule;
compiled = this.rule.Compile();
}
public string propertyName
{
get
{
return PropertyName(this.rule.Body as dynamic);
}
}
public bool Execute(T instance)
{
return compiled(instance);
}
private string PropertyName(BinaryExpression expression)
{
return PropertyName(expression.Left as dynamic);
}
private string PropertyName(MethodCallExpression expression)
{
if (expression.Object == null)
{
return PropertyName(expression.Arguments[0] as dynamic);
}
return PropertyName(expression.Object as dynamic);
}
private string PropertyName(MemberExpression expression)
{
string typeName = expression.Expression.Type.Name;
return typeName + "." + expression.Member.Name;
}
}
This is working for these tests:
private List<Tuple<Expression<Func<User, bool>>, string>> TestCases()
{
return new List<Tuple<Expression<Func<User, bool>>, string>> {
new Tuple<Expression<Func<User, bool>>, string>(
x => x.Skills.Count() > 0, "User.Skills"),
new Tuple<Expression<Func<User, bool>>, string>(
x => x.Skills.Where(s => s.Length > 0).Contains("pruning"), "User.Skills"),
new Tuple<Expression<Func<User, bool>>, string>(
x => x.Age > 20, "User.Age"),
new Tuple<Expression<Func<User, bool>>, string>(
x => x.Name != null, "User.Name"),
new Tuple<Expression<Func<User, bool>>, string>(
x => x.Name == "John", "User.Name"),
new Tuple<Expression<Func<User, bool>>, string>(
x => x.Skills.Contains("mowing"), "User.Skills")};
}