I am having trouble figuring out how to get server-side DbContext validation errors back to the client. I understand that Breeze has default validators that react to a few of the attributes such as Required, but what about all the other attributes? I could write a custom JavaScript validator for Breeze that will check on the client side, but I also need to check to make sure the entity is valid on the server-side.
For example, the application requires a Person to to have a valid email address. A malicious user comes along and gets an email address past the client and posts to the server with a data that would not pass the EmailAddress validator. Thus far my experience with Breeze is that the email address will save and not bubble up any DbContext Entity Framework errors.
Assuming the model below, what would be the best way to get any entity validation errors?
public class PeopleContext : DbContext
{
public PeopleContext()
: base("name=ConnectionString"){ }
public DbSet<Person> People { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[EmailAddress]
[Required]
public string Email { get; set; }
}
UPDATE 1:
Here are some instructions to re-create the issue that I am experiencing.
Follow the instructions to create the "Todo" sample (http://www.breezejs.com/documentation/start-nuget)
Add a new custom validator to the BreezeSampleTodoItem.cs file:
[AttributeUsage(AttributeTargets.Property)]
public class CustomValidator : ValidationAttribute
{
public override Boolean IsValid(Object value)
{
string val = (string)value;
if (!string.IsNullOrEmpty(val) && val == "Error")
{
ErrorMessage = "{0} equal the word 'Error'";
return false;
}
return true;
}
}
Decorate the Description field with the new custom validator:
[CustomValidator]
public string Description { get; set; }
Add the proper usings of course (System and System.ComponentModel.DataAnnotations).
Run the project.
In one of the description fields type "Error" and save.
This is where I would expect to see an error come up through Breeze, or even an DbEntityValidationException be thrown from Entity Framework. I have tried on 2 separate computers with the same result. The entity saves to the database as if there were no error. In fact, if you put a breakpoint anywhere inside IsValid method of the custom validator you will see that its not even being called.
As of Breeze v 0.78.1 all registered DbContext server side validations will now execute during an EntityManager SaveChanges call. Any exceptions encountered will cause the save to rollback, and any validation errors to be serialized back to the Breeze client.
Note that this functionality is not yet supported for older ObjectContext ( as opposed to DbContext) based EF models.
And ... thanks to adamlj for discovering this issue and suggesting the solution.
I'm not sure what you mean by
get server-side DbContext validation errors back to the client
You could mean that you want the validation error messages to be sent to the client. But the rest of your question suggests that you want to know (a) how to run a custom validation on the server and (b) how to acquire and run a corresponding JavaScript version of that validation on the client. I will address this interpretation of your question.
Server
The Entity Framework (which you're using in your example) automatically runs Data Annotation validation rules for you ... unless you've disabled that feature manually. If you create custom validation rules in the proper way, EF will run these as well. This post by Daniel Wertheim describes how to write such rules. I can't vouch for that post in every detail but it seems correct to me. It even defines a custom Email-validationattribute!
If authoring a custom Data Annotation validation rule seems too Baroque to you (as it often does to me), you can write and call your own validation logic in one of the BeforeSave... methods discussed in "Server-side Interception".
I think these are your best server options. On to the client ...
Client
Breeze registers client-side JavaScript validations to match certain of the server-side Data Annotations (e.g., Required and MaxLength) that come across the wire in the metadata. As I write, custom Data Annotations are not recognized nor included in the metadata and they have no out-of-the-box analogs on the client. If you want the client to prescreen your entities using these rules, you'll have to write your own corresponding JavaScript validators and register them for the pertinent entity types as discussed in the Validation documentation page.
If you have suggestions or better alternatives, we'd love to hear them.
Related
When I use Web (MVC), I always to create a separate classes layer. These classes often the same as DTO classes, but with attributes like [Display(Name = "Street")] and validation. But for web api Display attributes are not necessary, validation can be used by FluentValidation. Should Api controller returns ViewModels classes or DTO classes will be fine too?
the answer, as always is .... it depends.
If your API is serving multiple clients , apps etc, then returning DTOs is a better options.
ViewModels are specific to the MVC client and should already be prepared for display, meaning the data should already be formatted in a specific way, some fields maybe combined, they should satisfy whatever requirements the display pages have. They are called ViewNodels for a reason. The point is that they are rarely exactly the same as the data the API returns, which should be a bit more generic and follow a certain pattern to make sense to its users.
If your ViewModels are exactly the same and you only have one client then it's up to you if you want to create a set of duplicated classed just to avoid having the attributes.
Mapping from DTO to ViewModel and viceversa is not exactly complicated, but the process does introduce one more complication, one more layer.
Don't forget one thing though. API DTOs are supposed to return the data they have on any entity regardless of the requirements of any UI. Requirements can change anyway, new fields added or discarded. You're more than likely to leave the API alone when that happens and simply change your ViewModels.
Your ViewModels are specific to a UI page and should contain only the data required by that page. This means that you can end up with multiple ViewModels for the same data, it's just that the display requirements are different for each.
My vote goes towards keeping the ViewModels and DTOs separate, even if, at this point in time they are exactly the same. Thins always change and this is one of those things you can actually be ready for.
Actually it depends on application's architecture how we want to return response. In this case yes we can return DTO classes but i think that would not be the good approach because we should create a separate Resource classes that will map with DTO and then return. Just see the below example:
public class CustomerDTO
{
public int ID { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
}
public class CustomerResource
{
[JsonObject]
public string Name { get; set; }
[JsonObject]
public string Department { get; set; }
}
Suppose we have CustomerDTO class and we want to return response in the following json format
{
"name":"Abc xyz",
"department":"Testing"
}
So in this case we should we have separate class that will return as a response to the end user as i created CustomerResource. In this scenario we will create a mapper that will map DTO with resource object.
And also with this implementation we can test resources independently
I have an mvc3 create page using a View Model with 2 entities
like
class ViewModel1{
public User user{get;set;}
public Company company{get;set;}
}
where User and Company are EF4 entities(tables). I need to use a single page to create both(related) tables. Now the Company entity is optional under some conditions and I use jQuery to hide the corresponding section in the view.
However since company has required fields , the post back create function has ModelState.Valid as false.
What I want to do is if the Company section is hidden, I would like to skip validating the Company entity in ViewModel in Server( I avoid validation of hidden elements in Client).
Maybe there is a better and more proper approach to this?
What you have shown is not a view model. You call it a view model but it isn't because it is referencing your EF domain entities.
A more realistic view model would look like this:
class ViewModel1
{
public UserViewModel User { get;set; }
public CompanyViewModel Company { get; set; }
}
or even flattened out and containing only the properties that your view needs:
class ViewModel1
{
public int UserId { get;set; }
[Required]
public string FullUserName { get;set; }
[Required]
public string CompanyName { get; set; }
}
Now depending on your specific requirements about view model validation your UserViewModel and CompanyViewModel classes will be specifically designed for them.
Instead of putting the entities directly in the view model, put the properties for the entities in the view model and map between the view model and the actual entity objects on the server. That way you can control what properties are required for your view. Create some custom validation rules to validate that the required company properties are there when some company information is required. To do this on the server, you can have your view model implement IValidatableObject and implement the logic in the Validate method. On the client you can add rules to the jQuery validate plugin for the "required if" properties.
public class UserCreationViewModel : IValidatableObject
{
[Required]
public string Username { get; set; }
[Required]
public string FirstName { get; set; }
...
public string CompanyName { get; set; }
public string CompanyEmail { get; set; }
public IEnumerable<ValidationResult> Validate( ValidationContext context )
{
if (!string.IsNullOrEmpty(CompanyName) && string.IsNullOrEmpty(CompanyEmail))
{
return yield new ValidationResult("Company Email is required if you specify a company", new [] { "CompanyEmail" });
}
}
}
I'm not sure what I would do on the client-side. You have a choice of either adding specific rules to the validate plugin directly, but it might be hard to make it look exactly the same as using the unobtrusive validation that MVC adds. Alternatively, you could look at adding/removing the unobtrusive attributes from the "required if" elements using jQuery depending on the state of the elements that trigger their requirement. I suggest trying both ways -- look at the docs for the validate plugin to see how to add custom rules and examine the code emitted by the MVC framework for the unobtrusive validate to see what you would need to add to make that work.
Another possibility would be including/removing a partial view with the company properties in the from based on whether the company information is required or not. That is, type in a company name and use AJAX to grab the inputs required for the company and add them to the form. If the company name is deleted, delete the elements. Leave the server-side validation the way it is, but in the partial view mimic the HTML that the framework would add in for unobtrusive validation. This is sort of the best of both worlds as the jQuery code is much simpler and you get consistent validation, too.
There are many ways you can achieve,
1) more commonly donot use [Required] attribute on Company object, but have proper validation for parameters inside Company object.
In this case if Company object is null still validation will pass, but if Company object isnot null it will validate each properties.
2) If validation involves some complex business logic, then go for Self Validating Model. (inherit from IValiddatableObject, and override Validate(...).
3) By code, in the controller.
if(model.company == null)
this.ModelState.Keys.Where(k => k.Contains("company")).ToList().ForEach(k => this.ModelState.Remove(k));
first two are best approved approaches, third is just another way to achieve your functionalities
So, I am using ASP.NET MVC 3 and Entity Framework 4.1 (code-first).
I have a class like this:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
[Range(18, 99)]
public int Age { get; set; }
}
The range validation is fired correctly. But, for example, in some situations I would like to change the range for the Age attribute. Or even turn it off. How could I do it without changing my Model class? Is this possible to made programatically?
You can use the IValidatableObject interface and define custom validation rules.
See my answer at:
Using Data Annotations to make a field required while searching for another in a form mvc 3
Its usually just a matter of implementing the interface and determine when to enforce your rules.
I just realised the solution for this case.
Eg. A user can have an authorization to create a 14 years old person.
Before save the Model, we can invoke DataContext.GetValidationErrors() and infer if the only error validation is that we want to disable, and then set
DataContext.Configuration.ValidateOnSaveEnabled = false;
So, this way we are able to save the model.
Yes, it is possible to inject validators programmatically. Altering existing validators presents separate issues, as some attributes are read-only, so you may have to delete and replace your existing validator.
You can add a class to work on the validators by following my answer to this question.
At first sorry for Microsoft MVC team because of no simple solution for localizing data annotation validation messages. It takes me a lot of time and finally no simple solution found!
I finally decided to inherit from RequiredAttribute to do localization. so I did this :
public class MyRequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute
{
public MyRequiredAttribute()
: base()
{
this.ErrorMessageResourceType = typeof(Resources.DataAnnotationDefaults);
this.ErrorMessageResourceName = "Required";
}
}
where I provided my localized messages in DataAnnotationDefaults.resx file.
So I can use this simply
[MyRequired]
public int UnitCode { get; set; }
But the problem is: it doesn't apply in client-side and just in server-side. why? what did I miss?
It is amazing that the following line is ok and works in client-side too!
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(DataAnnotationDefaults))]
public int UnitCode { get; set; }
I will be glad and appreciated if someone can help me on this.
You need to understand how validation works in MVC and .NET. DataAnnotation is a generic validation library which can be used in all kinds of applications and not just MVC.
Hence MVC contains different types of adapters for MVC which is used to add support for DataAnnotations. The client-side adapters are specifically made for the attributes defined in System.ComponentModel.DataAnnotations.
You therefore need to create your own adapters to get it working with your derived attributes. I've written a blog entry about it.
The easier approach is to use my localized metadata provider.
I am working on an ASP.Net MVC3 application and I'm having trouble understanding the "right way" to do the validation I'm looking for.
For example, consider a model that looks like this:
[Required]
[StringLength(10, MinimumLength = 10)]
[RegularExpression("[0-9]{10}")]
public string Id { get; set; }
[Required]
public string Value { get; set; }
If I have an Id of "2342" and try to POST back, the model mapping kicks in and registers an error because of the length validation. However, if perform a GET against /controller/2342, then MVC seems to happily create a model with this invalid state (ModelState.Valid will be true). I could create some validations in the controller to catch this, but it seems redundant.
What is the best way to do this?
Thanks!
Jacob
When you perform a GET, you are simply retrieving a model with a given ID. So there is no validation performed. If you really want to make sure that requested model IDs should be 10 numbers in length, you should define constraint in Global.asax:
routes.MapRoute(
"Product",
"Product/{productId}",
new {controller="Product", action="Details"},
new {productId = #"[0-9]{10}" }
);
There is nothing in the framework that by default validates a model on a GET request as validation isn't generally required at that time. If you do want to force a validation here, this was answered in this prior question
See:
Validate model on initial request