xVal.ServerSide.RulesException.AddModelStateErrors alternative

Apr 7, 2009 at 12:15 PM
Edited Apr 7, 2009 at 12:18 PM

Hi,

xVal is working great for me so far, and I have another suggestion - this time about the xVal.ServerSide.RulesException.AddModelStateErrors methods. I'm a bit unhappy about these methods being part of the RulesException class, because it means I need to create an instance of an Exception just to be able to add the errors to the model:

var errors = DataAnnotationsValidationRunner.GetErrors(category);

if (errors.Any())
{
    new RulesException(errors).AddModelStateErrors(ModelState, "Item");
}

This feels unnatural to me: DataAnnotationsValidationRunner.GetErrors creates an error report for me, and in order to add those errors to the model I need an instance of an exception.  What I would like to write is this:

this.ModelState.AddModelStateErrors(DataAnnotationsValidationRunner.GetErrors(category), "Item");

Backed by an extension method like this:

public static void AddModelStateErrors(this ModelStateDictionary modelState, IEnumerable<ErrorInfo> errors, string prefix)
{
    prefix = (prefix == null) ? "" : (prefix + ".");
    foreach (ErrorInfo info in errors ?? new ErrorInfo[0])
    {
        ModelState state;
        string key = prefix + info.PropertyName;
        modelState.AddModelError(key, info.ErrorMessage);
        if (modelState.TryGetValue(key, out state) && (state.Value == null))
        {
            state.Value = new ValueProviderResult(null, null, null);
        }
    }
}
Coordinator
Apr 7, 2009 at 12:39 PM
Hi Dirk

I understand that it would be awkward if you had to construct an exception instance with no intention of actually throwing it. The reason that we assume you'll want an exception is that usually, you'll perform your validation somewhere in your model layer (which is separate from your MVC controller) so your model layer will want to signal the failure and communicate a failure reason to the controller by throwing an exception. That's one of the common ideas of model-based validation.

However, if you're calling the validation runner directly from a controller, I agree that you wouldn't want an exception at all. In that case, your extension method is a good approach.

Regards
Apr 7, 2009 at 12:54 PM
I see your point about the model layer communicating a validation error to the controller by throwing an exception.  However, I am hesitant about adding xVal code to my model - xVal is a UI technology and I prefer to keep UI technology out of my model.  In my model, I would prefer to implement IDataErrorInfo, and xVal would then receive its ErrorInfo objects by reading the content of IDataErrorInfo.

I guess this is mostly a matter of taste.

Dirk
Coordinator
Apr 7, 2009 at 1:04 PM
Naturally one of the xVal design goal is flexibility - it should be possible to get the benefits whatever approach you take to your architecture, so I'm glad you've got something that works for you.

xVal is designed mainly to assist model-based validation, by which I mean that your model defines and enforces its own rules. You won't ever have reason to put any of the UI-oriented xVal code into your model, so I'm not sure what you're referring to there. The typical setup is that you have your model signal rules failures in whatever way suits you (and you can do this with IDataErrorInfo, but that's not very robust, because it relies on cooperation with the UI layer, which is why I prefer exceptions). Since there isn't any suitable rules exception class baked into .NET, xVal includes the RulesException class for you to throw from your model layer. That's the server portion of xVal and I don't see any downside to using that in your model layer. It's not even tied to ASP.NET MVC - you can throw a RulesException from your model, and then catch it in a WebForms UI layer or a WinForms UI layer or a Silverlight UI layer.

The ability to copy RulesException contents to ModelState is just a convenience if you happen to be working with ASP.NET MVC, but that isn't one of the core ideas.
Apr 7, 2009 at 1:21 PM
Thanks, this clears up some "misconceptions" I had about xVal.  You are saying that xVal is about validating the model, and that there are a couple of convencience features for transporting the validation errors from the model to the UI (for example ASP.NET MVC).  Did I get that right?

Interestingly, I had a completely different view of xVal.  To me, xVal is/was something that assists me in performing validation and showing errors in the ASP.NET MVC part of my application.  Or in other words, xVal is an "add-on" to ASP.NET MVC.  And thus it felt natural to me to strictly separate xVal code and model code, just like I strictly separate ASP.NET MVC code and model code.

After thinking along the lines of "xVal is about validating the model" and asking myself what the "standard .net alternative" is, I have to concede that there is no standard .net alternative and it makes sense to consider xVal in the context of model validation.

A few words about IDataErrorInfo: I'm not sure what you mean by "it relies on cooperation with the UI layer".  Yes, the UI layer has to be aware of IDataErrorInfo and read its content, but that content is populated by the model itself, in response to changes in the properties of the model.  So I don't see the problem of robustness - or at least I haven't encountered any so far.

Thank you for your time,
Dirk
Apr 7, 2009 at 1:35 PM
Here's a tidbit that I find interesting with regard to our discussion: I was talking about using IDataErrorInfo to populate xVal, and here I found a blog where the other way around is discussed: populating IDataErrorInfo using xVal: http://schotime.net/blog/index.php/2009/03/05/validation-with-aspnet-mvc-xval-idataerrorinfo/

Dirk
Coordinator
Apr 7, 2009 at 1:58 PM
I think the misconception is the idea that xVal does server side-validation, or that DataAnnotationsValidationRunner is part of xVal - it isn't. DataAnnotationsValidationRunner is just a utility class I included in a demo project to show how you could use the DataAnnotations attributes to run validation on the server.

xVal is about detecting your server-side validation rules and running them on the client. xVal doesn't do server-side validation for you - it's up to you to use IDataErrorInfo or DataAnnotations or Castle Validation or NHibernate.Validator or whatever you want - although I have included the RulesException class which is a handy way to send an error report up the stack and then copy it into ModelState. xVal's real job is to detect declarative rules (e.g., rules expressed as attributes) and supply that configuration to a client-side validation framework such as jQuery.Validation.

A few words about IDataErrorInfo: I'm not sure what you mean by "it relies on cooperation with the UI layer"
I meant that with IDataErrorInfo, your UI layer has to specifically request an error report and take action on it. This isn't ideal because the UI layer could simply forget to request an error report, or could choose to ignore errors. I prefer to put the model in control over whether operations are allowed (so the UI layer has no choice but to comply), which works easily if the model is in the habit of aborting bad operations by throwing exceptions.