Reputation: 17898
I like to use nameof() in my ASP.NET Core application when setting mvc errors using ModelState.AddModelError(). This reduces hidden errors since I pass the name of an attribute here. Compared to a string, this has the benefit that a compiler error is thrown when the attribute was renamed.
Example
class MyModel{
public string Name{get;set;}
}
public class MyController:Controller{
public IActionResult Test(MyModel model) {
// Will hiddenly fail when "Name" is renamed to something else
ModelState.AddModelError("Name", "The Name field is required!");
// Better: Using the auto renaming feature of VS, this will be updated if "Name" was renamed
ModelState.AddModelError(nameof(model.Name), "The Name field is required!");
}
}
This works, until I have another entity:
class MyModel{
public string Name{get;set;}
public MyOtherModel OtherModel{get;set;}
}
class MyOtherModel{
public string OtherName{get;set;}
}
Now I want to get the name of OtherModel
. The problem: ASP.NET requires BaseClass.ChildClass pattern here, so OtherModel.OtherName
in this example. Using the same logic like above:
public class MyController:Controller{
public IActionResult Test(MyModel model) {
ModelState.AddModelError(nameof(model.MyOtherModel.OtherName), "The Name field is required!");
}
}
will just give me the name of the child attribute, so OtherName
in this example. But I need OtherModel.OtherName
. Is there a clean way to get this without building the string itself?
This would be possible doing something like this:
string actionName = nameof(model.MyOtherModel) + "." + nameof(model.MyOtherModel.OtherName);
But not a very clean and intuitive way imho.
Upvotes: 3
Views: 828
Reputation: 3190
A tweak to Neme's answer.
static string NameOfMember<TModel, TResult>(Expression<Func<TModel, TResult>> accessor)
{
string str = accessor.Body.ToString();
return str.Substring(str.IndexOf('.') + 1);
}
Usage:
ModelState.AddModelError(NameOfMember<Model, DateTime?>(model => model.Foo.Bar.Baz),
"Baz is bad.");
Upvotes: 0
Reputation: 503
As others have suggested, you will probably have to write your own method to do this. This is what I came up with using Linq.Expressions:
string NameOfMember(Expression<Func<Model, object>> accessor)
{
string str = accessor.Body.ToString();
return str.Substring(str.IndexOf('.') + 1);
}
You would then use it like this: NameOfMember(model => model.MyOtherModel.OtherName)
I'm not familiar with ASP.NET but you can change Model to any type you want, including object.
As a side note, you shouldn't repeat "The Name field is required!" in your code. Maybe you can include that in your helper method as well.
Upvotes: 3