Creating a Typed ModelState.AddModelError ()

In the controller, I can perform a database search, etc. and add the error message associated with the model property:

public ActionResult CreateJob(CreateJobModel viewModel) { var call = FindCall(viewModel.CallNumber); if (call == null) { ModelState.AddModelError("CallNumber", "Idiot User!"); } } 

I do not like that CallNumber is a string, ideally it should refer directly to viewModel.CallNumber, and if I change the name of this property, it should also be changed.

How can I achieve this?

I would suggest that the code end with something like this, which will lead to an expression for accessing the properties:

 AddModelFieldError(() => viewModel.CallNumber, "Idiot User!"); 

But I'm not sure how to create such a method, or in case it is a sub / internal property that needs an error message.

+4
source share
2 answers

I would write my own general 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 use:

 ModelState.AddModelError<CreateJobModel>(x => x.CallNumber, "some kind attention"); 

It is slightly different from the version you asked for, but I hope this can be an acceptable alternative.

+7
source

With 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!"); } } 
+1
source

All Articles