In a controller I can perform DB lookups etc and add some error message associated with a model property:
public ActionResult CreateJob(CreateJobModel viewModel)
{
var call = FindCall(viewModel.CallNumber);
if (call == null)
{
ModelState.AddModelError("CallNumber", "Idiot User!");
}
}
I don't like that CallNumber is a string, when ideally it should refer directly to viewModel.CallNumber, and if I change the name of that property, it should be changed too.
How can I achieve this?
I'd imagine the code would end up something like this, which would take a property access expression:
AddModelFieldError(() => viewModel.CallNumber, "Idiot User!");
But I'm not sure how to create a method like that, or in the case where it's a sub/inner-property that needs the error message.
I would write my own generic extension method:
public static class ModelStateDictionaryHelper
{
public static void AddModelError<TViewModel>(
this ModelStateDictionary me,
Expression<Func<TViewModel, object>> lambdaExpression, string error)
{
me.AddModelError(GetPropertyName(lambdaExpression), error);
}
private static string GetPropertyName(Expression lambdaExpression)
{
IList<string> list = new List<string>();
var e = lambdaExpression;
while (true)
{
switch (e.NodeType)
{
case ExpressionType.Lambda:
e = ((LambdaExpression)e).Body;
break;
case ExpressionType.MemberAccess:
var propertyInfo = ((MemberExpression)e).Member as PropertyInfo;
var prop = propertyInfo != null
? propertyInfo.Name
: null;
list.Add(prop);
var memberExpression = (MemberExpression)e;
if (memberExpression.Expression.NodeType != ExpressionType.Parameter)
{
var parameter = GetParameterExpression(memberExpression.Expression);
if (parameter != null)
{
e = Expression.Lambda(memberExpression.Expression, parameter);
break;
}
}
return string.Join(".", list.Reverse());
default:
return null;
}
}
}
private static ParameterExpression GetParameterExpression(Expression expression)
{
while (expression.NodeType == ExpressionType.MemberAccess)
{
expression = ((MemberExpression)expression).Expression;
}
return expression.NodeType == ExpressionType.Parameter ? (ParameterExpression)expression : null;
}
}
and the usage:
ModelState.AddModelError<CreateJobModel>(x => x.CallNumber,
"some kind attention");
It looks a bit differently from the version you've asked about, but I hope it can be acceptable alternative.
As of C# 6, you can use the nameof
operator.
public ActionResult CreateJob(CreateJobModel viewModel)
{
var call = FindCall(viewModel.CallNumber);
if (call == null)
{
ModelState.AddModelError(nameof(CreateJobModel.CallNumber), "Idiot User!");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With