Domain Model & View Model Validation

Feb 10, 2009 at 5:27 PM
Hey Mike,

I have a ( domain model / view model / wpf ) architecture and would just like to find out how VA could be best applied so that the validation is only defined on the domain model and flows through to the UI? I have seen your article "Domain Model Validation + WPF" which seems to only cater for a ( domain model / wpf ) approach so I wondered if this was even possible (i.e. with the view model acting as a "proxy")?

Frank
Coordinator
Feb 10, 2009 at 8:16 PM
Hi,
Sure, it's possible - interception validation works such that when a property setter (for example) on the domain model is invoked, the value is validated and if validation fails a validation exception is thrown. Depending on how you've designed the relationship between your domain model, view model/controller, wpf view, this exception could be caught by the WPF UI.

It's difficult to provide you with more info than this without knowing how your implementation of how these components fit together - do view model properties directly map to domain model properties?
If you wanted to do the validation in the view model without invoking the domain model you can get hold of the validators and invoke them separate from the domain model - the domain model will not be updated:

var value = ...
IPropertyInfo property = myModel.GetProperty("MyProperty")
IValidator v = property.ResolveValidator();
if (v != null)
{
  try
  {
    MethodBase method = property.GetSetMethod();
    v.Invoke(value, new ValidationContext(myModel, myModel.GetType(), property, method, method.GetParameters()[0], new [] { value }));
  }
  catch (ValidationException e)
  {
    ...
  }
}

constructing the ValidationContext is a bit of a pain. I will be overloading the constructor so that you only have to provide an instance and a PropertyInfo.

please let me know if you need more info of how to make VA work with your architecture.

Mike
Feb 11, 2009 at 9:05 AM
Hi Mike,

Firstly I really appreciate you taking the time to answer my questions. Many thanks!

My view model mostly maps directly to my domain model, but there may be exceptions (depending on the UI / domain model). My current architecture has a view model object (inheriting from DependencyObject) which takes the domain model object in via the constructor which then sets the DependencyProperty(ies) of the view model based on the domain model object values. I am also currently using PropertyChangedCallback(s) to keep the underlying domain model object in sync with the changes in the view model. This 'keeping in sync' could potentially change on a project-by-project basis (i.e. if I decide to rather do certain validation in the view model first and then only update the domain model once validated), which is where your code snippet would come in really handy.

My domain model will also be invoked via Windows Services and WCF in the future, which is why I would like to keep the validation at the domain model level. Are there any considerations for using VA with WCF or should it just be good to go?

Otherwise, from what I have seen the framework looks great so I'm going to start trying to integrate it now...

Frank
Feb 11, 2009 at 9:35 AM
PS - I dont suppose it is at all possible to define validation on interfaces?
Coordinator
Feb 11, 2009 at 11:02 AM
I don't have any experience with WCF so I can't answer your question. If you find VA isn't a good fit please let me know and I'll look into it.

I took a design decision that validation is an implementation detail and as such (IMO) it doesn't make sense to place validation on an interface. By doing so you're saying you know how every future implementation of that interface works and that your validation on the interface is correct for these unknown future implementations.
Having said that, it is possible to register Type validation on an interface because it's a Type. What this means is if you called myModel.Validate(), it would invoke all validators registered to typeof(Model) and typeof(IModel) and any other Types in the hierarchy.


Feb 23, 2009 at 11:47 AM
Hey Mike,

I have the interception validation working well within my domain model, and along with your ValidatesOnExceptionsBinding the UI is updated nicely when validation fails - however I still have one outstanding issue which may be more around the design of the approach I am using which I am hoping you may have come across before and have a simple solution.

If you recall I wanted my domain model to contain all the validation. When I enter an invalid value into a textbox, the PropertyChangedCallback is fired and the underlying domain model is updated with the entered value.If the value is invalid, the interception validation kicks in and the validation exception is thrown, which is handled by the binding and a nice error is shown. The problem now is that the view model and domain model are now no longer in sync, so if I then want to object.validate() before save then the validation succeeds (as the domain model was not updated due to the invalid value).

I guess I could write some sort of mechanism into my view model to manage the state of the validation on the underlying domain model but I wondered if you had a better solution already contained within the framework?

Thanks,
Frank
Coordinator
Feb 24, 2009 at 7:49 AM
Hi Frank,
are you calling object.Validate() with the intention of identifying if the UI controls have valid values? To do this you can query the GetHasError(...) method of the the Validation AttachedProperty for each UI control. It's not clear to me what you have cached in the viewmodel that would become out-of-sync with the model unless you're duplicating the content of the model, and updating the viewmodel content before updating the model where the validation occurs.
Feb 24, 2009 at 9:38 AM
Hi Mike,

I am calling object.validate() within a view model object so cant use the GetHasError as the UI controls are effectively a layer higher in the architecture.

For example I have a view model object which encapsulates the functionality of the UI form. On that form I have a list of items which can then each have their details changed. Each item is bound to another specific view model object, which acts as a wrapper around a domain model object. The content of the domain model is duplicated in the view model (usually 1-to1 mapping but could vary). On construction of the view model object, each dependency property on the VM is initialised with the value from the DM.

I then have PropertyChangedCallbacks on the VM so that when a value is changed by the user via the UI, then the DM is updated (to keep in sync). If the value is invalid, then the interception validation keeps the DM from getting updated and the two then become out of sync. I could change it to apply the VM content to DM on save only, but I like the validation appearing in 'real-time' as the user is making changes. Its almost as though I need the DM to be updated and then the interception validation to be done after the value has been set. This ofcourse would only apply when making UI changes, whereas if the DM is used via something like a Windows Service, then interception validation can function as normal.

Any thoughts?

Frank
Coordinator
Feb 24, 2009 at 10:14 AM
Thanks for the description - that makes sense to me now.

So we need to establish what exactly the issue is:

1) Interception/state validation is set at a global level, but could be optionally set per (model) Type at runtime? This is doable with a code change, but I think this could add some confusion - when I set a property on my model, could it become invalid? is interception validation enabled? This sounds more like a workaround than a solution.
2) The ViewModel is not validated. Validation occurs on the DM but the VM has already been set. Can you update the DM in the VM property setter before assigning the backing field? If this isn't possible because the VM properties are dependency properties, add interception validation to the property. This could easily be achieved with a new validator [ValidateAs("MyModel.MyProperty")] which resolves the validation on the property passed in the attribute and invokes it. So for your VM, you'd need to decorate it with the [Validate] attribute (for PostSharp interception) and decorate your VM properties with the ValidateAs(...) validator, providing the property name of the matching DM property. Would this work for you?

let me know what you think

Mike
Feb 24, 2009 at 12:48 PM
I agree with you on option 1, so would prefer option 2.

I am using dependency properties so using the Setter approach  means that it is only fired on first initialisation (during the VM object construction as described before) and then it does not fire again when changes are made in the UI, whereas the PropertyChangedCallback does fire.

I quite like the idea of the new ValidateAs validator as it allows some simple decoration to my VM to inherit all the validation defined in my DM. Just one question, could you have an overload on ValidateAs that allows you to define the property such as TypeOf<MyType>.GetProperty(o => o.Property) so that you have compile time checking?
Coordinator
Feb 24, 2009 at 1:56 PM
Can you paste a snippet here of what an example VM with dependency properties looks like as you're currently using, and also what you would like it to look like if you had use of a ValidateAs attribute?
Feb 24, 2009 at 2:58 PM
View Model Snippet:

    public class ViewModel<T> : DependencyObject
        where T : DomainModel
    {
        private T DomainModel
        { get; set; }

        // Constructor
        public ViewModel(T dm)
        {
            this.DomainModel = dm;
            this.DomainModel.Property = dm.Property;
        }

        // Dependency property
        public static readonly DependencyProperty DependencyProperty =
            DependencyProperty.Register("Property", typeof(string), typeof(ViewModel<T>),
            new PropertyMetadata(new PropertyChangedCallback(OnPropertyChanged)));

        // View model property
        public string Property
        {
            get { return this.GetValue(DependencyProperty).ToString(); }
            set { this.SetValue(DependencyProperty, value); }
        }

        // Property changed callback
        private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var vm = sender as ViewModel<T>;
            // Update domain model entity with value from view model property
            vm.DomainModel.Property = vm.Property;
        }
    } 

ValidateAs Attribute:

The following syntax would be ideal...

ValidateAs<DomainModel>(dm => dm.Property);
public string Property
{ get; set; }

..but from a quick attempt it seems that you cant use generics with attributes so the syntax may need to be more like:

ValidateAs(typeof(DomainModel).GetProperty(dm => dm.Property));
public string Property
{ get; set; }

Also, if the standard VM property is not called from the UI and only the dependency property is, then I assume that the attribute would need to be able to be defined on both, with the dependency property one hooking into the ValidateValueCallback?
Coordinator
Feb 24, 2009 at 6:44 PM
sorry I don't have time to try this out right now to give you an immediate answer but there are a few things you could experiment with.

1) in OnPropertyChanged(...) place the call to set the DM property within a try/catch block. If a validation exception is caught, set the DP back to the old value as provided by the DependencyPropertyChangedEventArgs and throw again. There's a performance cost to this in that the DP value and DM proeprty is set and validated twice if the first value is invalid, but as it is driven by user entry (I'm assuming) it may be acceptable to you.
or
2) you can achieve the same as the ValidateAs attribute by using the code in my first reply. There's a simpler constructor for ValidationContext now for properties. Put this in the setter of the ViewModel<T>.Property property before calling this.SetValue(...). This should be handled ok by the UI.
3) you can programatically register a validator on this.SetValue(...) which calls the validator on the DM property. Generally this might not be such a great idea as the DP could be set by anything in the property system and the validation exception may not be gracefully handled, but it might be ok in this example.
4) similarly you could maybe put validation in CoerceValueCallback but I doubt WPF will gracefully cope with an exception thrown here

let me know how you get on. If the ValidateAs validator could work here I'll put it on the backlog.

Mike
Feb 25, 2009 at 9:55 AM
Edited Feb 25, 2009 at 4:23 PM
Thanks Mike. I will give those options a try and let you know my findings...
Feb 25, 2009 at 4:24 PM
<style> Body{font-family: Verdana; font-size: 0.75em;}#ThreadNotificationFooter{border-top: 1px solid #ccc; color: gray;}#ThreadNotificationPostBody{Margin-Bottom: 2em;} </style>
I have started trying to implement validation on an object which inherits from a ViewModel<T> type class and got the following exception:
 
Error 5 Unhandled exception: System.ArgumentException: You cannot add an aspect to a generic method instance or to a method in a generic type instance. Add the aspect to the corresponding generic method definition.
   at PostSharp.Laos.LaosReflectionAspectCollection.AddAspect(MethodBase targetMethod, ILaosMethodLevelAspect aspect)
   at ValidationAspects.PostSharp.ValidateAttribute.ProvideAspects(Object target, LaosReflectionAspectCollection collection)
   at PostSharp.Laos.CompoundAspect.PostSharp.Laos.ILaosReflectionAspectProvider.ProvideAspects(LaosReflectionAspectCollection collection)
   at PostSharp.Laos.Weaver.LaosTask.EnqueueAspects(ILaosReflectionAspectProvider provider)
   at PostSharp.Laos.Weaver.LaosTask.Execute()
   at PostSharp.Extensibility.Project.ExecutePhase(String phase)
   at PostSharp.Extensibility.Project.Execute()
   at PostSharp.Extensibility.PostSharpObject.ExecuteProjects()
   at PostSharp.Extensibility.PostSharpObject.InvokeProjects(ProjectInvocation[] projectInvocations)
   at PostSharp.MSBuild.PostSharpRemoteTask.Execute(PostSharpTaskParameters parameters, TaskLoggingHelper log) Presentation

The error occurs the moment I place the Validate attribute on a class inheriting from a generic. Are generics supported?
Coordinator
Feb 25, 2009 at 4:34 PM
Yes, generics are supported for PostSharp interception. See TestInterceptionValidation.cs in TestValidationAspects.PostSharp

What version of PostSharp are you using? If you're using 1.5 you need to use the latest dev build and not CTP 2
Feb 26, 2009 at 12:38 PM
Edited Feb 26, 2009 at 12:42 PM
Hi Mike,

I'm having the same issue as above. Using VA with PostSharp, if you apply the Validate attribute to a closed type that inherits from a generic base, you get the exception Frank mentions above. Your test doesn't cover this since GenericDummy<T> is a generic type definition rather than a closed type inheriting from a generic type. If you created a new type, say, GenericDummyString : GenericDummy<string> and applied Validate to that type, you'd see the error when the PostSharp post-compile step runs.

All of my domain types inherit from a common generic base, so for example User : DomainBase<User>. This is necessary because within the base classes there are methods for getting repository and service instances for each domain type, and to do this you have to know the type in the base. User itself is closed and is not generic. I can apply Validate to DomainBase<T>, but not to User. It doesn't appear to be a problem with PostSharp per-se (in that you can apply an attribute derived from CompoundAspect to a closed type instance). The exception happens when PostSharp calls ProvideAspects in the Validate attribute. Exactly what that problem is I don't know as I've not been able to drill down far enough (if anyone knows how to debug into code that's run when PostSharp runs the post-compile step, I'd be interested to know <g>).

I'm using PostSharp 1.0, not 1.5; it may be that the problem is gone in 1.5, I will try it at some point soon, but I wanted to point out that the scenario Frank mentions isn't actually covered by your test and you might like to extend it to cover it. If the problem still occurs with PostSharp 1.5 then the problem looks to be on the ValidationAspects side.

I'll carry on looking at this and let you know if I spot anything that might be of help...

Neil Hewitt
Coordinator
Feb 26, 2009 at 2:04 PM
Edited Feb 26, 2009 at 2:16 PM
It's a PostSharp issue but as the exception states, it might be expected behaviour. It can be reproduced with the following which does not use any VA code:

    [Serializable]
    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    [MulticastAttributeUsage(MulticastTargets.Class, AllowMultiple = false)]
    public class ApplyAspectAttribute : CompoundAspect
    {
        public override void ProvideAspects(object target, LaosReflectionAspectCollection collection)
        {
            collection.AddAspect(((Type)target).GetMethod("Method"), new MethodAspect());
        }
    }

    [Serializable]
    public class MethodAspect : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionEventArgs eventArgs) { }
    }

    public class DomainBase<T>
    {
        public void Method() {}
    }

    [ApplyAspect]
    public class Closed : DomainBase<string>
    { }

reproduced referencing PostSharp 1.5 CTP

I'll query Gael
Feb 26, 2009 at 3:35 PM
Mike,

Thanks - yes, I'd realised it couldn't be VA after a further dig and a test case similar to the one you provided here. The exception message is a little ambiguous, but my reading of it is that it's something that should apply to methods rather than to types. If it can't do this then it probably should - this is a very common pattern for domain objects to share a common base ancestor and have default properties and methods that must be strongly-typed.

I only abandoned Unity because we're using NHibernate and it seems to be impossible to use interception on persistent types since NH does a specific type check and won't accept a proxy or derived type instance on session flush (you can override the NH object creation step to get intercepted instances from the container, but when you try to save them - epic fail). PostSharp does the trick since it weaves the code in at compile-time; so if we can get this to work, my problems are sorted. And PostSharp promises to solve a few other problems for us.

VA is a nice library, BTW - one of my devs found it and was quickly hooked, so I thought I'd adopt it for our latest project. So much better than writing dreary business rule code in every method to just have an attribute to validate the parameters. I look forward to using it more :-)

Neil

Coordinator
Feb 27, 2009 at 8:11 AM
Just a quick update to anyone who's following this thread - I'm currently discussing this issue with Gael Fraiteur (PostSharp) to see how it can be resolved. I'll keep you posted.
Coordinator
Feb 27, 2009 at 9:17 AM
ok, with Gael's help, I think I have a solution to this. It will be a VA internal change and will not require changes to client code. Hopefully I will have it checked-in today.
Coordinator
Feb 27, 2009 at 10:04 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Coordinator
Feb 27, 2009 at 12:54 PM
Issue fixed in CS#25519. Please let me know how you get on with it.

Mike
Feb 27, 2009 at 1:40 PM
Mike, that works perfectly for me. Nice and neat encapsulation of a thorny problem there.

Neil



Mar 5, 2009 at 11:23 AM
HI Mike,

The fix works for me too, thanks very much.

With regards to the options that you suggested to experiment with, I tried each of them with limited success, but finally got it working with my own ValidateAs attribute, so I have validation applied to my VM which uses the validation defined on my DM. :) Interception validation is used at the DM level, so my DM objects are always valid, and then I use a combination of interception and state validation at the VM to ensure every validation scenario is catered for . I also implemented your IDataErrorInfo solution from the samples, which is applied to my VM objects and now everything is working perfectly!

I just have one question - is there any way to validate collection properties - or shall I just write my own to iterate through and validate each object?

Frank
Coordinator
Mar 5, 2009 at 1:13 PM
Thanks Frank - good it hear it works.

Is it possible for you to post some code of how you managed to get VM validation working off of the DM? I think others might find it useful. I'm about to start a new project using Prism (which uses VMs) so I'll probably have the same requirement as you and would really appreciate some guidance from someone who's already solved this validation scenario.

There's nothing yet in VA to validate items of a collection -I'll put an issue on the backlog. When writing your own you'll find registering a lambda validation function for the specific collection type the easiest. A more general case of a ValidatorFactory enumerating a collection of any type is doable but will require more work. A colleague who needed to validate an IEnumerable<Data> passed to a method did not contain any null items. To achieve this he wrote a lambda validation function something like:
typeof(DM).GetMethod("Analyse").GetParameters()[0].AddValidation<IEnumerable<Data>>((value, context) =>
{
  foreach (Data d in value)
  {
    if (d == null)
      throw new ValidationException("item is null");
  }
});
and put it in the DM static constructor. This is off the top of my head so might not compile.

Mike
Mar 20, 2009 at 4:03 PM
Hey Mike,

I've only been working on this validation implementation whenever I get chance to (background project) so its been dragged out a bit. I finally have the project pretty much done and the following are some of the approaches I had to take - which may be helpful to someone. They may not be the best approach, but they do work. Code snippets posted where appropriate:

1.) ValidateAs factory, used with ValidateAs attribute
I created a ValidateAs attribute and factory, but one drawback is that it currently only works on system types. For example, a property that is of type VM.Entity cannot be converted to DM.Entity to apply the validation. Perhaps an interface could be used, but in my case, the VM may not always have the same interface as DM.

    public class ValidateAs : ValidatorFactoryBase<object>
    {
        public PropertyInfo Property
        { get; set; }

        public ValidateAs(PropertyInfo property)
        {
            this.Property = property;
        }

        protected override Action<object, IValidationContext> ValidateAction
        {
            get
            {
                return (value, context) =>
                {
                    var validator = this.Property.ResolveValidator();
                    if (validator != null)
                        validator.Invoke(value, new ValidationContext(context.Instance, this.Property, value));
                };
            }
        }
    }

Used as:

    [ValidateAs(typeof(Domain.Model.Entity),"Name")]
    public string Name
    { get; set;}

Another drawback is the fact that you have to define the property as a string. Unfortunately the use of attributes is quite limiting.

2.) I use Xceed DataGrids, and I came across a scenario where I wanted a validation message to be shown on the grid as a whole (bound to list of VM objects), where the validation message was about the state of the list of objects, and not pertaining to any individual item. For example, I wanted to ensure that each item in the list had a unique name, and then show the validation message about the grid. Xceed's DataGrid doesnt support Validation.ErrorTemplate, so I ended up creating a container control (ValidationContainer inheriting from DockPanel) to wrap the DataGrid (done in Xaml). This container had a dummy DependencyProperty (DummyProperty) which would need to be bound to any property on the VM, so that I could programmatically fire the validation error message. I could then call object.Validate() in the VM, and then feed any validation exceptions back to the view to update the UI - i.e. IView.NotifyError(exception).

The view would then contain an implementation of the interface as such:

            public void NotifyError(Exception exception)
            {
                var bindExpression = BindingOperations.GetBindingExpression(GridValidation, ValidationContainer.DummyProperty);
                if (bindExpression != null)
                    Validation.MarkInvalid(bindExpression, GetValidationError(bindExpression, exception));
            }

            public void ClearError()
            {
                var bindExpression = BindingOperations.GetBindingExpression(GridValidation, ValidationContainer.DummyProperty);
                if (bindExpression != null)
                    Validation.ClearInvalid(bindExpression);
            }

Its quite a workaround, but I found there were instances where I needed to show a validation message in the UI, but didnt have any binding context that the validation applied to - especially as I am using RoutedCommands with the execution of the command defined in the VM. Any ideas on a better approaches would be appreciated.

3.) Your implementation of IDataErrorInfo worked fine when binding to a single object, but when a control was bound to a list property, the IDataErrorInfo was only invoked when that property was set and not when items were added/removed - so I had to use the CollectionChanged event to manually object.Validate(). Not sure if I was doing something wrong?

Frank