I’m having trouble figuring out why Breeze.js (0.84.3) does not return error messages to the client that are set by Custom Validation Attributes applied on the server model at the class level. I am able to reproduce this with the Breeze TODO KO sample as follows:
using System;
using System.ComponentModel.DataAnnotations;
namespace Todo.Models
{
[MyCustomValidator] // NEW
public class TodoItem
{
public int Id { get; set; }
[Required, StringLength(maximumLength: 30)]
public string Description { get; set; } // Set to 'error' to trigger server error.
public System.DateTime CreatedAt { get; set; }
public bool IsDone { get; set; }
public bool IsArchived { get; set; }
}
[AttributeUsage(AttributeTargets.Class)] // NEW
public class MyCustomValidator : ValidationAttribute
{
public override Boolean IsValid(Object value)
{
var todo = value as TodoItem;
if (todo.Description == "error")
{
ErrorMessage = "The TodoItem is not valid!";
return false;
}
return true;
}
}
}
I would expect that the error message “The TodoItem is not valid!” would be returned to the client however Breeze seems to always return “Value cannot be null. Parameter name: source” from the server. It seems there is an exception happening at:
StackTrace:
at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable`1 source, Func`2 selector) at Breeze.WebApi.EFContextProvider`1.SaveChangesCore(Dictionary`2 saveMap)
at Breeze.WebApi.ContextProvider.SaveChanges(JObject saveBundle)
at Todo.Controllers.TodosController.SaveChanges(JObject saveBundle) in c:\Users\RichardH\Downloads\Software\Web\breeze-runtime-plus-0.84.3\Samples\Todo\Todo\Controllers\TodosController.cs:line 41
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.<ExecuteAsync>b__4()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
Can anyone shed some light on how I can apply validations on the server to the entire model (not just to a single property/field)?
Thanks,
Richard
First, Breeze.NET server-side metadata does not communicate custom server validation attributes to the Breeze client. It only conveys certain standard ones such as Required, MaxLength, and StringLength. If you want to duplicate validations on the client, you’ll have to write validators in JavaScript on the client and register them with the client-side metadata as explained in the Validation documentation
Second, I guess that the failure is taking place within EF as it attempts to use your custom validation rule. The exception is thrown inside SaveChangesCore which is where the EFContextProvider asks EF to save changes.
Per the stack trace, it’s probably a LINQ exception arising from the
Selectstatement where Breeze is trying to tell you about the validation error. I mean theSelectin herevar formattedKey = key.EntitySetName + ";" + key.EntityKeyValues.Select(v => v.ToString()).ToAggregateString(" ,");The “value cannot be null…” message suggests that
key.EntityKeyValuesis null. I cannot explain how your entity got here without key values. I guess you could set a breakpoint and find out.For our part, we need to make this line less vulnerable. I’ll put that on our list of things to fix.