Creating a typed ModelState.AddModelError() - c#

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!");
}
}

Related

Retrieving nested PropertyInfo via expressions

I'm trying to create a function where I can pass in an expression to say which properties I'm interested in. I have it working for top level properties but not for nested properties.
Example model
public class Foo {
public string Name { get; set; }
public List<Foo> List { get; set; }
}
What I have so far
private PropertyInfo GetPropertyInfo<TModel>(Expression<Func<TModel, object>> selector)
{
if (selector.NodeType != ExpressionType.Lambda)
{
throw new ArgumentException("Selector must be lambda expression", nameof(selector));
}
var lambda = (LambdaExpression)selector;
var memberExpression = ExtractMemberExpression(lambda.Body);
if (memberExpression == null)
throw new ArgumentException("Selector must be member access expression", nameof(selector));
if (memberExpression.Member.DeclaringType == null)
{
throw new InvalidOperationException("Property does not have declaring type");
}
return memberExpression.Member.DeclaringType.GetProperty(memberExpression.Member.Name);
}
private static MemberExpression ExtractMemberExpression(Expression expression)
{
if (expression.NodeType == ExpressionType.MemberAccess)
{
return ((MemberExpression)expression);
}
if (expression.NodeType == ExpressionType.Convert)
{
var operand = ((UnaryExpression)expression).Operand;
return ExtractMemberExpression(operand);
}
return null;
}
So:
GetPropertyInfo<Foo>(x => x.Name); // works
GetPropertyInfo<Foo>(x => x.List.Select(y => y.Name); <-- how do I get this?
I'm looking for a way to pick any property from a complex object.
You need to extend your ExtractMemberExpression just a bit to accept Select call expression:
private MemberExpression ExtractMemberExpression(Expression expression) {
if (expression.NodeType == ExpressionType.MemberAccess) {
return ((MemberExpression) expression);
}
if (expression.NodeType == ExpressionType.Convert) {
var operand = ((UnaryExpression) expression).Operand;
return ExtractMemberExpression(operand);
}
if (expression.NodeType == ExpressionType.Lambda) {
return ExtractMemberExpression(((LambdaExpression) expression).Body);
}
if (expression.NodeType == ExpressionType.Call) {
var call = (MethodCallExpression) expression;
// any method named Select with 2 parameters will do
if (call.Method.Name == "Select" && call.Arguments.Count == 2) {
return ExtractMemberExpression(call.Arguments[1]);
}
}
return null;
}

C# How to get nameof a sentence as String [duplicate]

I'm developing a API that uses lambda expressions to specify properties. I'm using this famous piece of code similar to this one (this is simplified and incomplete, just to make clear what I'm talking about):
public void Foo<T, P>(Expression<Func<T, P>> action)
{
var expression = (MemberExpression)action.Body;
string propertyName = expression.Member.Name;
// ...
}
To be called like this:
Foo((String x) => x.Length);
Now I would like to specify a property path by chaining property names, like this:
Foo((MyClass x) => x.Name.Length);
Foo should be able to split the path into its property names ("Name" and "Length"). Is there a way to do this with reasonable effort?
There is a somehow similar looking question, but I think they are trying to combine lambda expressions there.
Another question also is dealing with nested property names, but I don't really understand what they are talking about.
Something like this?
public void Foo<T, P>(Expression<Func<T, P>> expr)
{
MemberExpression me;
switch (expr.Body.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = expr.Body as UnaryExpression;
me = ((ue != null) ? ue.Operand : null) as MemberExpression;
break;
default:
me = expr.Body as MemberExpression;
break;
}
while (me != null)
{
string propertyName = me.Member.Name;
Type propertyType = me.Type;
Console.WriteLine(propertyName + ": " + propertyType);
me = me.Expression as MemberExpression;
}
}
I played a little with ExpressionVisitor:
public static class PropertyPath<TSource>
{
public static IReadOnlyList<MemberInfo> Get<TResult>(Expression<Func<TSource, TResult>> expression)
{
var visitor = new PropertyVisitor();
visitor.Visit(expression.Body);
visitor.Path.Reverse();
return visitor.Path;
}
private class PropertyVisitor : ExpressionVisitor
{
internal readonly List<MemberInfo> Path = new List<MemberInfo>();
protected override Expression VisitMember(MemberExpression node)
{
if (!(node.Member is PropertyInfo))
{
throw new ArgumentException("The path can only contain properties", nameof(node));
}
this.Path.Add(node.Member);
return base.VisitMember(node);
}
}
}
Usage:
var path = string.Join(".", PropertyPath<string>.Get(x => x.Length).Select(p => p.Name));
Old question, I know... but if it's only the names you need, an even simpler way to do it is:
expr.ToString().Split('.').Skip(1)
EDIT:
public class A
{
public B Property { get; set; }
}
public class B
{
public C field;
}
[Fact]
public void FactMethodName()
{
var exp = (Expression<Func<A, object>>) (x => x.Property.field);
foreach (var part in exp.ToString().Split('.').Skip(1))
Console.WriteLine(part);
// Output:
// Property
// field
}
I have a shared .NET standard DTO between client and server and expressions are a great way to create query strings that can be rebuilt and executed Api side.
A perfect way to create Type safe queries over the wire.
I digress, I needed a property path also
x => x.Siblings.Age
To produce a string like
"Siblings.Age"
I went with this
public static string GetMemberPath(MemberExpression me)
{
var parts = new List<string>();
while (me != null)
{
parts.Add(me.Member.Name);
me = me.Expression as MemberExpression;
}
parts.Reverse();
return string.Join(".", parts);
}
public static string GetPath<T, TProperty>(this Expression<Func<T, TProperty>> exp)
{
return string.Join(".", GetItemsInPath(exp).Reverse());
}
private static IEnumerable<string> GetItemsInPath<T, TProperty>(Expression<Func<T, TProperty>> exp)
{
if (exp == null)
{
yield break;
}
var memberExp = FindMemberExpression(exp.Body);
while (memberExp != null)
{
yield return memberExp.Member.Name;
memberExp = FindMemberExpression(memberExp.Expression);
}
}
I refactored these answers to use recursion, so no need for order reversal. I only need the property tree, so left out all but MemberExpressions. Should be simple to add functionality if need be.
public IEnumerable<string> PropertyPath<TModel, TValue>(
Expression<Func<TModel, TValue>> expression)
{
if (expression.Body is MemberExpression memberExpression)
return PropertyPathRecurse(memberExpression);
return Enumerable.Empty<string>();
}
private IEnumerable<string> PropertyPathRecurse(MemberExpression? expression)
{
if (expression is null)
return Enumerable.Empty<string>();
return PropertyPathRecurse(expression.Expression as MemberExpression)
.Append(expression.Member.Name);
}

Passing property list as strongly typed parameters

I have this method which extracts the property name from an expresssion:
private static string GetPropertyName<TObj, TProp>(Expression<Func<TObj, TProp>> prop)
{
var expression = prop.Body as MemberExpression;
if (expression != null)
{
var property = expression.Member as PropertyInfo;
if (property != null)
{
return property.Name;
}
}
return string.Empty;
}
So later I can use it like this:
GetPropertyName((User u) => u.Surname); //Returns "Surname"
I would like to be able to pass a collection of properties instead one by one. Just to be clear, the properties could be of different types.
I am completely agree with #Patrick and its preferred way over mine.
but if you say you are not using the C#6.0 and then you can use the code you have written. I just use the param, yield return and one foreach loop
private static IEnumerable<string> GetPropertyName<TObj, TProp>(params Expression<Func<TObj, TProp>>[] propCollection)
{
foreach (var prop in propCollection)
{
var expression = prop.Body as MemberExpression;
if (expression != null)
{
var property = expression.Member as PropertyInfo;
if (property != null)
{
yield return property.Name;
}
}
yield return string.Empty;
}
}
UPDATE
First One ask you to specific the type of the object again and again mean you have to provide the full length expression again.
Try the below it will ask you to specify the property as much as you want in one Expression only.
public static IEnumerable<string> GetPropertiesName<TObj, TProp>(Expression<Func<TObj, TProp[]>> prop)
{
var array = (prop.Body as NewArrayExpression);
var exp = array == null ? null : array.Expressions;
if (exp != null)
{
//var expArr = (prop.Body as NewArrayExpression).Expressions;
foreach (var oneProp in exp)
{
Expression onePropExp;
if (oneProp.GetType() == typeof (UnaryExpression))
{
onePropExp = (oneProp as UnaryExpression).Operand;
}
else
{
onePropExp = oneProp;
}
var property = (onePropExp as MemberExpression).Member as PropertyInfo;
if (property != null)
{
yield return property.Name;
}
yield return string.Empty;
}
}
yield return string.Empty;
}
You can call it like -
var propNames = GetPropertiesName((AllSubsTransAndDevices d) => new[]
{
d.CurrentDriverId,
d.GPSDevicesId,
d.TransporterId
});
It might be me, but I think you don't need to do this the hard way. You can simply use the C# 6 nameof keyword. This assumes you can use C# 6 of course.
string name = nameof(u.Surname);
Try this:
Usage: string[] props = GetPropertiesName((MainWindow m) => m.Lalala, (MainWindow m) => m.Lalala);
private static string[] GetPropertiesName<TObj, TProp>(params Expression<Func<TObj, TProp>>[] prop)
{
List<string> ret = new List<string>();
foreach (var item in prop)
ret.Add(GetPropertyName(item));
return ret.ToArray();
}
private static string GetPropertyName<TObj, TProp>(Expression<Func<TObj, TProp>> prop)
{
var expression = prop.Body as MemberExpression;
if (expression != null)
{
var property = expression.Member as PropertyInfo;
if (property != null)
{
return property.Name;
}
}
return string.Empty;
}

Getting sub property names strongly typed

With databinding objects to controls and grids I hated how the property names would be magic strings, so I created a very simple method as follows:
public static string GetPropertyName<PropertyType>(Expression<Func<T, PropertyType>> expressionForProperty)
{
MemberExpression expression = expressionForProperty.Body as MemberExpression;
return expression.Member.Name;
}
This lets me use code such as:
Product.GetPropertyName(m => m.Name)
to return "Name".
This works perfectly for basic objects. However if I change the method call to be:
Product.GetPropertyName(m => m.ProductCategory.Name)
This also returns "Name". But in order for the databinding to work, I would need it to return "ProductCategory.Name". Is there a way I can get to this by changing the method "GetPropertyName"?
A possible workaround would be to do this:
string test = Product.GetPropertyName(p => p.ProductCategory) + "." + ProductCategory.GetPropertyName(pc => pc.Name)
However, this isn't a neat solution.
This is a modified version of something I might have found here on StackOVerflow:
public static class GetPropertyNameExtension
{
public static string GetPropertyName<TArg, TProperty>(this Expression<Func<TArg, TProperty>> propertyExpression)
{
return propertyExpression.Body.GetMemberExpression().GetPropertyName();
}
public static string GetPropertyName<TProperty>(this Expression<Func<TProperty>> propertyExpression)
{
return propertyExpression.Body.GetMemberExpression().GetPropertyName();
}
public static string GetPropertyName(this MemberExpression memberExpression)
{
if (memberExpression == null)
{
return null;
}
if (memberExpression.Member.MemberType != MemberTypes.Property)
{
return null;
}
var child = memberExpression.Member.Name;
var parent = GetPropertyName(memberExpression.Expression.GetMemberExpression());
return parent == null ?
child
: parent + "." + child;
}
public static MemberExpression GetMemberExpression(this Expression expression)
{
if (expression is MemberExpression)
return (MemberExpression)expression;
if (expression is UnaryExpression)
return (MemberExpression)((UnaryExpression)expression).Operand;
return null;
}
}
I came up with the following which seems to work:
public static string GetComplexPropertyName<PropertyType>(Expression<Func<T, PropertyType>> expressionForProperty)
{
// get the expression body
Expression expressionBody = expressionForProperty.Body as MemberExpression;
string expressionAsString = null;
// all properties bar the root property will be "convert"
switch (expressionBody.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
UnaryExpression unaryExpression = expressionBody as UnaryExpression;
if (unaryExpression != null)
{
expressionAsString = unaryExpression.Operand.ToString();
}
break;
default:
expressionAsString = expressionBody.ToString();
break;
}
// makes ure we have got an expression
if (!String.IsNullOrWhiteSpace(expressionAsString))
{
// we want to get rid of the first operand as it will be the root type, so get the first occurence of "."
int positionOfFirstDot = expressionAsString.IndexOf('.');
if (positionOfFirstDot != -1)
{
return expressionAsString.Substring(positionOfFirstDot + 1, expressionAsString.Length - 1 - positionOfFirstDot);
}
}
return string.Empty;
}

C#: Getting Names of properties in a chain from lambda expression

I'm developing a API that uses lambda expressions to specify properties. I'm using this famous piece of code similar to this one (this is simplified and incomplete, just to make clear what I'm talking about):
public void Foo<T, P>(Expression<Func<T, P>> action)
{
var expression = (MemberExpression)action.Body;
string propertyName = expression.Member.Name;
// ...
}
To be called like this:
Foo((String x) => x.Length);
Now I would like to specify a property path by chaining property names, like this:
Foo((MyClass x) => x.Name.Length);
Foo should be able to split the path into its property names ("Name" and "Length"). Is there a way to do this with reasonable effort?
There is a somehow similar looking question, but I think they are trying to combine lambda expressions there.
Another question also is dealing with nested property names, but I don't really understand what they are talking about.
Something like this?
public void Foo<T, P>(Expression<Func<T, P>> expr)
{
MemberExpression me;
switch (expr.Body.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = expr.Body as UnaryExpression;
me = ((ue != null) ? ue.Operand : null) as MemberExpression;
break;
default:
me = expr.Body as MemberExpression;
break;
}
while (me != null)
{
string propertyName = me.Member.Name;
Type propertyType = me.Type;
Console.WriteLine(propertyName + ": " + propertyType);
me = me.Expression as MemberExpression;
}
}
I played a little with ExpressionVisitor:
public static class PropertyPath<TSource>
{
public static IReadOnlyList<MemberInfo> Get<TResult>(Expression<Func<TSource, TResult>> expression)
{
var visitor = new PropertyVisitor();
visitor.Visit(expression.Body);
visitor.Path.Reverse();
return visitor.Path;
}
private class PropertyVisitor : ExpressionVisitor
{
internal readonly List<MemberInfo> Path = new List<MemberInfo>();
protected override Expression VisitMember(MemberExpression node)
{
if (!(node.Member is PropertyInfo))
{
throw new ArgumentException("The path can only contain properties", nameof(node));
}
this.Path.Add(node.Member);
return base.VisitMember(node);
}
}
}
Usage:
var path = string.Join(".", PropertyPath<string>.Get(x => x.Length).Select(p => p.Name));
Old question, I know... but if it's only the names you need, an even simpler way to do it is:
expr.ToString().Split('.').Skip(1)
EDIT:
public class A
{
public B Property { get; set; }
}
public class B
{
public C field;
}
[Fact]
public void FactMethodName()
{
var exp = (Expression<Func<A, object>>) (x => x.Property.field);
foreach (var part in exp.ToString().Split('.').Skip(1))
Console.WriteLine(part);
// Output:
// Property
// field
}
I have a shared .NET standard DTO between client and server and expressions are a great way to create query strings that can be rebuilt and executed Api side.
A perfect way to create Type safe queries over the wire.
I digress, I needed a property path also
x => x.Siblings.Age
To produce a string like
"Siblings.Age"
I went with this
public static string GetMemberPath(MemberExpression me)
{
var parts = new List<string>();
while (me != null)
{
parts.Add(me.Member.Name);
me = me.Expression as MemberExpression;
}
parts.Reverse();
return string.Join(".", parts);
}
public static string GetPath<T, TProperty>(this Expression<Func<T, TProperty>> exp)
{
return string.Join(".", GetItemsInPath(exp).Reverse());
}
private static IEnumerable<string> GetItemsInPath<T, TProperty>(Expression<Func<T, TProperty>> exp)
{
if (exp == null)
{
yield break;
}
var memberExp = FindMemberExpression(exp.Body);
while (memberExp != null)
{
yield return memberExp.Member.Name;
memberExp = FindMemberExpression(memberExp.Expression);
}
}
I refactored these answers to use recursion, so no need for order reversal. I only need the property tree, so left out all but MemberExpressions. Should be simple to add functionality if need be.
public IEnumerable<string> PropertyPath<TModel, TValue>(
Expression<Func<TModel, TValue>> expression)
{
if (expression.Body is MemberExpression memberExpression)
return PropertyPathRecurse(memberExpression);
return Enumerable.Empty<string>();
}
private IEnumerable<string> PropertyPathRecurse(MemberExpression? expression)
{
if (expression is null)
return Enumerable.Empty<string>();
return PropertyPathRecurse(expression.Expression as MemberExpression)
.Append(expression.Member.Name);
}

Categories

Resources