5

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.

  1. Follow the instructions to create the "Todo" sample (http://www.breezejs.com/documentation/start-nuget)
  2. 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;
        }
    }
    
  3. Decorate the Description field with the new custom validator:

    [CustomValidator]
    public string Description { get; set; }
    
  4. Add the proper usings of course (System and System.ComponentModel.DataAnnotations).

  5. Run the project.
  6. 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.

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
adamlj
  • 225
  • 1
  • 9

2 Answers2

3

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.

Jay Traband
  • 17,053
  • 1
  • 23
  • 44
1

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.

Ward
  • 17,793
  • 4
  • 37
  • 53
  • Hi Ward, Thanks for the reply, and awesome project! I have edited my question to provide an example of how to replicate the issue I am experiencing. The problem I have run into is that none of the Data Annotations are being evaluated when using the EFContextProvider.SaveChanges(saveBundle) method. I did pull down the source code for Breeze and if I change code for the EFContextProvider.SaveChangesCore method use ((DbContext)(object)Context).SaveChanges() instead of ObjectContext.SaveChanges(), then Annotations are evaluated correctly. But I would rather not modify the Source. – adamlj Dec 13 '12 at 20:06
  • One more note, I did check out the BeforeSave methods. My concern there is that I can only return a bool. For validation errors there should be some way to get an error message back to the user. – adamlj Dec 13 '12 at 20:15
  • You should throw an exception that includes the explanation of failure, e.g., `throw new InvalidOperationException("Cannot save entity of unknown type");` Returning false merely tells the ContextProvider to skip that entity during save; it does not terminate the save! See the "[Server-side interception](http://www.breezejs.com/documentation/server-side-interception)" page in the docs. – Ward Dec 14 '12 at 10:59
  • For more elegant error signaling, consider [this bit of guidance](http://www.codeguru.com/csharp/.net/net_asp/handling-exceptions-in-asp.net-web-api.htm) on how to send error info to Web API clients – Ward Dec 14 '12 at 11:21
  • And backing up a bit, I see your point about using DbContext! I will get some attention on this. Stay tuned. – Ward Dec 14 '12 at 11:24
  • To be clear, you should expect EF to choke on that exception. You should not expect Breeze to do so on the client because it doesn't know a thing about your CustomValidator. You'd have create and register a corresponding JS version if you wanted it applied on the client. That is secondary to the issue you've uncovered. – Ward Dec 14 '12 at 11:33
  • Thanks for the article about exception handling in the Web API clients. Lots of cool ideas I had not thought about. I'm glad you were able to reproduce the problem, well, lets put 'glad' in bunny-ears. Thanks for the help. Staying-tuned. – adamlj Dec 14 '12 at 16:26
  • @adamlj - I'm going to put some energy into guidance for exception handling "soon". [See my comment here](http://stackoverflow.com/a/13887969/200253). – Ward Dec 15 '12 at 00:08