Validation on View Model using Model - validation

I have ViewModels and Models.
ViewModels use Models.
I have 1 ViewModel for 1 Controller.
ex:
public class ReferentielFournisseursViewModel
{
public Fournisseur monFournisseur { get; set; }
[...]
public Adresses ListeSites { get; set; }
}
public class Fournisseur
{
public int NoFournisseur { get; set; }
public string Name { get; set; }
public string Sigle { get; set; }
[Required]
public string Siren { get; set; }
}
In this case, I use validation by annotation: "Required" on the model.
But I have to create an other page using the same model without this validation.
public class DemandeDePrixViewModel
{
public Fournisseur monFournisseur { get; set; }
[...]
public Commande CommandeEnCours { get; set; }
}
How I can use Validation on the ViewModel instead of the Model ?

You should look into the IValidateableObject interface:
public class Model: IValidatableObject
{
public int Property { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (Property < 5)
{
results.Add(new ValidationResult("Prop1 must be larger than 5"));
}
return results;
}
}
This code will automatically run when ModelState.IsValid is called
See also: https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.ivalidatableobject?view=netcore-3.1

Related

ModelState.Isvalid, why does it validate relatetd Tabels?

This is the properties to validate. (Code from my cshtml.cs (Razor))
[BindProperty]
public TMedProductStorageLocation TMedProductStorageLocation { get; set; }
[BindProperty]
public IList<TStorageLocation> TStorageLocations { get; set; }
[BindProperty]
public int SelectedStorageLocationId { get; set; }
[BindProperty]
public TMedProduct SelectedProduct { get; set; }
[BindProperty]
public int RelocateAmount { get; set; }
These are the properties I want to Validate. But It also Validates:
public class TSubstanceGroup
{
public TSubstanceGroup()
{
TMedProducts = new HashSet<TMedProduct>();
}
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Geben Sie eine Bezeichnung ein.")]
public string Name { get; set; }
public ICollection<TMedProduct> TMedProducts { get; set; }
}
because this is a related table to TMedProduct.
How can I avoid validation for related tables?
Not sure what is your razor pages design,but you need to know that ModelState will validate all the models you define in razor pages backend not due to you have relationship between models:
public class PrivacyModel : PageModel
{
[BindProperty]
public TSubstanceGroup TSubstanceGroup { get; set; }
[BindProperty]
public TMedProduct SelectedProduct { get; set; }
[BindProperty]
public TMedProductStorageLocation TMedProductStorageLocation { get; set; }
[BindProperty]
public IList<TStorageLocation> TStorageLocations { get; set; }
[BindProperty]
public int SelectedStorageLocationId { get; set; }
[BindProperty]
public int RelocateAmount { get; set; }
public IActionResult OnPost()
{
//...
}
public void OnGet()
{
//..
}
}
To avoid validation for related tables,you could remove the validation with the key:
public IActionResult OnPost()
{
ModelState.Remove("KeyName");
if(ModelState.IsValid)
{
//...
}
return Page();
}
For how to know the key name,you could check in the ModelState:

Why Entity Framework replaces provided value with a new incremental value

I'm trying to work my way through a Dot Net Core MVC project, but I am facing a problem I couldn't solve.
And honestly, I didn't know what to search for.
The project is a simple vehicle registry,
Each vehicle has a Make, a Model, a some Features.
Here are the domain models:
public class Make
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Model> Models { get; set; }
public Make()
{
Models=new Collection<Model>();
}
}
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public Make Make { get; set; }
public int MakeId { get; set; }
public Model()
{
Make=new Make();
}
}
public class Feature
{
public int Id { get; set; }
public string Name { get; set; }
}
[Table(name:"VehicleFeatures")]
public class VehicleFeature
{
public int VehicleId { get; set; }
public int FeatureId { get; set; }
public Feature Feature { get; set; }
public Vehicle Vehicle { get; set; }
public VehicleFeature()
{
Feature=new Feature();
Vehicle=new Vehicle();
}
}
public class Vehicle
{
public int Id { get; set; }
public Model Model { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ModelId { get; set; }
public ICollection<VehicleFeature> VehicleFeatures { get; set; }
public string ContactName { get; set; }
public string ContactPhone { get; set; }
public string ContactEmail { get; set; }
public DateTime LastUpdate { get; set; }
public bool IsRegistered { get; set; }
public Vehicle()
{
Model=new Model();
VehicleFeatures=new Collection<VehicleFeature>();
}
}
The problem is that when I send the following request to the corresponding controller, EF is replacing the provided value with a new incremental value.
The request I send:
{
"ModelId":10,
"VehicleFeatures":[
{"featureId":45},
{"featureId":46}
],
"ContactName":"Alice",
"ContactPhone":"1234",
"ContactEmail":"Alice#local",
"LastUpdate":"1980-01-01",
"IsRegistered":true
}
And this is what happens:
The controller receives correct values,
Correct values are added to the context,
And then suddenly all hell breaks loose.
This is the controller code:
[HttpPost]
public async Task<IActionResult> CreateVehicle([FromBody] Vehicle v)
{
context.Vehicles.Add(v);
await context.SaveChangesAsync();
return Ok(v.ModelId);
}
What am I missing here?

Custom Model binder :Can't bind parameter 'xxx' because it has conflicting attributes on it

I am using a custom model binder in order to put some custom logic in my model Binding.
Here's my DTOs:
public class TaskDto
{
public int Id { get; set; }
[MaxLength(100), Required]
public string Name { get; set; }
public List<StepDto> Steps { get; set; }
public List<ResourceDTO> Resources { get; set; }
}
Step DTO:
public class StepDto
{
public int Id { get; set; }
[MaxLength(100)]
public string Description { get; set; }
public StepType StepType { get; set; }
}
ResourceDTO:
public class ResourceDTO
{
public int Id { get; set; }
[MaxLength(250), Required]
public string Title { get; set; }
[Required]
public string Link { get; set; }
public string MetaTagUrl { get; set; }
[Required, Range(1, 1)]
public ResourceType ResourceType { get; set; }
}
Where ResourceType is an enumeration (only has 1 as value for now.)
I tried creating a custom model binder using this link.
Here's my API Action method signature:
[HttpPut]
[Route("datacapture/{activityId}/tasks")]
[Authorize]
public async Task<IHttpActionResult> UpdateAllDataCaptureActivities(int activityId, [FromBody][ModelBinder] TaskDto tasks)
{
...
}
I am getting the following error on calling the API:
"Message": "An error has occurred.",
"ExceptionMessage": "Can't bind parameter 'tasks' because it has conflicting attributes on it.",
"ExceptionType": "System.InvalidOperationException",
Don't use [FromBody] and [ModelBinder] at the same time.

Map a complex MVC ViewModel in knockout

I have a MVC view which contains observables and observablearrays. What is the proper way to map?
My MVC ViewModel looks similar to this:
public class CategorysEditViewModel
{
public Category Categorys { get; set; }
public IEnumerable<Expert> candidates { get; set; }
public IEnumerable<Expert> experts { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Policy { get; set; }
}
public class Expert
{
public int ExpertID { get; set; }
public int CategoryID { get; set; }
public string Name { get; set; }
public string ExpertGUID { get; set; }
}
Assuming the cshtml file is strongly typed to the CategorysEditViewModel, you can serialize the view model object to JSON and then use the ko.mapping tool to map it to a knockout viewmodel.
Check out this post: Best practice on passing Mvc Model to KnockoutJS

Should I inherit models in my view model or use a property for the model

I have a handful of email templates and in each template I have a header and footer that all share the same info.
The header and footer are represented by EmailModel.cs
public class EmailModel
{
public string CompanyName { get { return ConfigurationManager.AppSettings["CompanyName"]; } }
public string PhoneNumber { get { return ConfigurationManager.AppSettings["PhoneNumber"]; } }
public string FacebookUrl { get { return ConfigurationManager.AppSettings["FacebookUrl"]; } }
public string TwitterUrl { get { return ConfigurationManager.AppSettings["TwitterUrl"]; } }
public string YouTubeUrl { get { return ConfigurationManager.AppSettings["YouTubeUrl"]; } }
//Additional methods for sending these templates as emails
}
Now for a specific email template I have a view model.NewSignUpEmailViewModel.cs
Should I do this:
public class NewSignUpEmailViewModel : EmailModel
{
public string FirstName { get; set; }
public string CompanyName { get; set; }
public string UserName { get; set; }
public Guid UserId { get; set; }
}
or this:
public class NewSignUpEmailViewModel
{
public EmailModel Email {get; set;}
public string FirstName { get; set; }
public string CompanyName { get; set; }
public string UserName { get; set; }
public Guid UserId { get; set; }
}
I just used email as an example, is there pros/cons to each?
The only con I can see is that in some cases you will run into duplicate property name issue.
Composition is often preferred over inheritance, but both have their place. One good rule of thumb is to determine if there is an "is-a" or a "has-a" relationship between your objects. If object 1 has object 2 as a component, composition is definitely the way to go.
As an example, let's approach your data model a bit differently:
public class SocialLinks
{
public string FacebookUrl { get; set; }
public string TwitterUrl { get; set; }
public string YouTubeUrl { get; set; }
}
public class User
{
public SocialLinks links { get; set; }
public string Name { get; set; }
// and so on
}
In this example, it's obvious that a user HAS social web links, as opposed to the user being a specialized version of the SocialLinks class. Hope that helps!

Resources