ASP.NET Core 3 remote validation with EF Core 3

asp.net-core-mvc asp.net-mvc data-annotations entity-framework entity-framework-core

Question

Bit of an EF beginner's question really, but I'm running into a problem using the [Remote] validation using ASP.NET MVC Core 3 with EF Core 3.

Following one of Microsoft's posts, it suggested keeping everything "DRY" (don't repeat yourself). So as directed, I added my data annotations for validation to my 'entity' class (the DbSet in my context). This feels kinda odd, mixing Presentation logic in with my DB logic, but I also like writing things just once so thought I'd give it a try.

Regarding my entity. It has a many-to-many relationship with another entity. So let's say my entity is "Person", well, there's a list of cars that this person can own.

My Create method

In the past with MVC, I've always written my 'view model' to contain just what is needed for databinding in the View. However, wanting to keep things DRY, I obviously need to use my Person entity class for binding in the View since that is where my DataAnnotations live. It's got various other properties that I don't need the user to fill in, so I guess I just don't have to expose them on the UI (though I recognise that this won't stop a hacker providing me that information...but that's another topic.)

For creating simple input boxes, this works perfectly, but I need to show the user a list of cars to pick from. So, where do I get the list of available cars from as it's not on my entity? I got around this by creating a wrapping ViewModel class similar to this:

public class MyViewModel
{
    public Person Entity {get; set;}
    public List<Car> Cars {get;set;}
}

Now I have to do my data binding to the property I've called "Entity" and I can pull the additional cars data from the other property.

This works well too. But, I then run into a problem when I do my validation.

So my Person class has the following (make-believe) property

public class Person
{
    [Remote("CheckIdIsAvailable","Persons")]
    public string Id { get; set; }
}

So, recalling that my instance of Person is the Entity property of my ViewModel, the View therefore has the following in the form:

<tr>
    <td>ID</td>
    <td><input asp-for="Entity.Id" /></td>
    <td><span asp-validation-for="Entity.Id" class="text-danger"></span></td>
</tr>

In my controller, I have the validation method:

[HttpGet]
public async Task<IActionResult> CheckIdIsAvailable(string Id)
{
    ....
}

However, the value comes as "null" because the parameter sent over the wire is "...?Entity.Id=ABCD" and my string is called 'Id' and not 'Entity.Id'.

So, I'm either missing something obvious here, or going about this the wrong way.

1
0
9/30/2019 4:55:06 PM

Accepted Answer

You could just receive Entity.Id in an object in the remote validation action:

[HttpGet]
 public async Task<IActionResult> CheckIdIsAvailable(Person entity)
{
  string Id = entity.Id;
  //other logic
}

For model binding, the parameter' s name is entity

1
10/1/2019 8:01:20 AM

Popular Answer

As Chris Pratt said, you're correct in that you should use a ViewModel object for binding to and from the view.

It's worth looking at the HTML that the tag helper has generated for you and noticing that the input will have a name attribute with whatever property you linked it to in that object (the asp-for="" part). The name property is how the model binding from the view to the Controller knows how to map your object coming back from the view.

But ideally, you'll send a ViewModel object to the view after populating it and expecting one back and map to and from your entities on either side without sending/exposing your entities to the outside world.

public async Task<IActionResult> CheckIdIsAvailable(PersonViewModel model)
{

}

You then add your validation to the ViewModel properties and not to your Entity.

The only attributes you should really have in your Entity classes are if you're using Code First and you're defining the fields in the database - like a [MaxLength()] Attribute for string fields (which is good practice because by default I think EF makes string fields NVARCHAR(MAX) if you don't define them, which isn't great for lookups later on. But that's besides the point).

There are other ways to define the field properties than attributes as well which lead to 'cleaner' entity definitions, but that's also not really the question here.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow