views:

824

answers:

3

I'm currently using the ModelStateDictionary in asp.net mvc to hold validation errors and pass then back to the user. Being able to check if the whole model is valid with ModelState.IsValid is particularly. However, a current application I'm working on has a need to be able to report warnings. These aren't as critical so the form content can still be saved, but they should be shown to the user so that action can be optionally taken.

I've been looking through the framework to see if there are any obvious place to extend it to allow me to do this. I'm thinking that another dictionary with warnings in and a subclass of model error called model warning. I'm not sure how I'd get the framework to use my new container classes in the view etc. though, I still want all of the existing error stuff to work.

If anyone has tried anything similar or has any thoughts I'd appreciate their input.

Update:

I've got as far as extending the ViewDataDictionary to add some warnings

public class AetherViewDataDictionary : ViewDataDictionary
{
 public AetherViewDataDictionary()
 {
  ModelStateWarning = new ModelStateDictionary();
 }

 public AetherViewDataDictionary(object model) : base(model)
 {
  ModelStateWarning = new ModelStateDictionary();
 }

 public AetherViewDataDictionary(ViewDataDictionary viewDataDictionary) : base(viewDataDictionary) 
 {
  ModelStateWarning = new ModelStateDictionary();
 }

 public ModelStateDictionary ModelStateWarning { get; private set; }
}

The problem that I'm having now is that when I get to my view code, this is just for debug I'm losing the fact that its my new type, so when I try to cast it back and get access to my new dictionary I have no joy.

public partial class Index : ViewPage<PageViewData>
{
 protected override void SetViewData(ViewDataDictionary viewData)
 {
  base.SetViewData(viewData);
 }
}

It sets it correctly here, but when I check the type its gone.

Edit: This turned out to be a dumb way of doing things, see answer below.

+2  A: 

Why not simply add a list of warnings, or a dictionary, to the ViewData and then display them in your view?

e.g.

ViewData[ "warnings" ] = new[] { "You need to snarfle your aardvark" } ;
Mike Scott
A: 

So the route that I was headed down before turned out to be a bad idea, there just isn't enough access in the framework to get at the bits that you need. At least not without reinventing the wheel a few times.

I decided to head down the route of extending the ModelState class to add a warnings collection to it:

public class AetherModelState : ModelState
{
 public AetherModelState() { }

 public AetherModelState(ModelState state)
 {
  this.AttemptedValue = state.AttemptedValue;

  foreach (var error in state.Errors)
   this.Errors.Add(error);
 }

 private ModelErrorCollection _warnings = new ModelErrorCollection();

 public ModelErrorCollection Warnings { get { return this._warnings; } }
}

In order to be able to easily add warnings in the same way that you would errors I created some extension methods for the ModelStateDictionary:

public static class ModelStateDictionaryExtensions
{
 public static void AddModelWarning(this ModelStateDictionary msd, string key, Exception exception)
 {
  GetModelStateForKey(key, msd).Warnings.Add(exception);
 }

 public static void AddModelWarning(this ModelStateDictionary msd, string key, string errorMessage)
 {
  GetModelStateForKey(key, msd).Warnings.Add(errorMessage);
 }

 private static AetherModelState GetModelStateForKey(string key, ModelStateDictionary msd)
 {
  ModelState state;
  if (string.IsNullOrEmpty(key))
   throw new ArgumentException("key");

  if (!msd.TryGetValue(key, out state))
  {
   msd[key] = state = new AetherModelState();
  }

  if (!(state is AetherModelState))
  {
   msd.Remove(key);
   msd[key] = state = new AetherModelState(state);
  }

  return state as AetherModelState;
 }

 public static bool HasWarnings(this ModelStateDictionary msd)
 {
  return msd.Values.Any<ModelState>(delegate(ModelState modelState)
  {
   var aState = modelState as AetherModelState;
   if (aState == null) return true;
   return (aState.Warnings.Count == 0);
  });
 }
}

The GetModelStateForKey code is ropey but you should be able to see where I'm headed with this. The next thing to do is to write some extension methods that allow me to display the warnings along with the errors

Simon Farrow
A: 

Simon, were you able to find a suitable solution to your problem w/o having to hack a work-around? We're having similar problems with errors and warnings (as well as other 2-3 custom informational model messages). I believe that MS will include some form of custom model messages in MVC2 RC, so that might be a start. Otherwise, the hack can get really messy fairly quickly (using a similar approach to what you were using).

Syrahn