Question

I'm working on a project using WPF and MVVM with Entity Framework 4.3, and I would like to know how can I perform business logic validation implementing the IDataErrorInfo interface.

All of my models (POCO classes) are implementing it in order to perform raw validations, like maxlength, non-negative numbers, and so on...

But what about bussiness logic validation, such as to prevent duplicate records?

Imagine I have a textbox for a material "reference", which must be unique, defined liked this:

 <TextBox Text="{Binding Material.Reference, ValidatesOnDataErrors=True, NotifyOnValidationError=true, 
                                 UpdateSourceTrigger=PropertyChanged}">

The model will successfully validate the reference's length, but if there's already a material in my viewmodel's materials observablecollection, how should I notify the user of this fact from my ViewModel, yet taking advantage of the IDataErrorInfo messages?

Was it helpful?

Solution

I've done this in the past by exposing a validation delegate from my models that my ViewModels can hook into for additional business logic validation

The end result ends up looking like this:

public class MyViewModel
{
    // Keeping these generic to reduce code here, but they
    // should be full properties with PropertyChange notification
    public ObservableCollection<MyModel> MyCollection { get; set; }
    public MyModel SelectedModel { get; set; }

    public MyViewModel()
    {
        MyCollection = DAL.GetAllModels();

        // Add the validation delegate to each object
        foreach(var model in MyCollection)
            model.AddValidationErrorDelegate(ValidateModel);
    }

    // Validation Delegate to verify the object's name is unique
    private string ValidateObject(object sender, string propertyName)
    {
        if (propertyName == "Name")
        {
            var obj = (MyModel)sender;
            var existingCount = MyCollection.Count(p => 
                p.Name == obj.Name && p.Id != obj.Id);

            if (existingCount > 0)
                return "This name has already been taken";
        }
        return null;
    }
}

Most of my models inherit from a generic base class, which includes this validation delegate. Here's the relevant code from that base class, taken from my blog article on Validating Business Rules in MVVM

#region IDataErrorInfo & Validation Members

/// <summary>
/// List of Property Names that should be validated.
/// Usually populated by the Model's Constructor
/// </summary>
protected List<string> ValidatedProperties = new List<string>();

#region Validation Delegate

public delegate string ValidationErrorDelegate(
    object sender, string propertyName);

private List<ValidationErrorDelegate> _validationDelegates = new List<ValidationErrorDelegate>();

public void AddValidationErrorDelegate(
    ValidationErrorDelegate func)
{
    _validationDelegates.Add(func);
}

#endregion // Validation Delegate

#region IDataErrorInfo for binding errors

string IDataErrorInfo.Error { get { return null; } }

string IDataErrorInfo.this[string propertyName]
{
    get { return this.GetValidationError(propertyName); }
}

public string GetValidationError(string propertyName)
{
    // Check to see if this property has any validation
    if (ValidatedProperties.IndexOf(propertyName) >= 0)
    {
        string s = null;

        foreach (var func in _validationDelegates)
        {
            s = func(this, propertyName);
            if (s != null)
                return s;
        }
    }

    return s;
}

#endregion // IDataErrorInfo for binding errors

#region IsValid Property

public bool IsValid
{
    get
    {
        return (GetValidationError() == null);
    }
}

public string GetValidationError()
{
    string error = null;

    if (ValidatedProperties != null)
    {
        foreach (string s in ValidatedProperties)
        {
            error = GetValidationError(s);
            if (error != null)
                return error;
        }
    }

    return error;
}

#endregion // IsValid Property

#endregion // IDataErrorInfo & Validation Members

This allows me to keep the basic data validation in my Models, and my ViewModels can attach any customized business logic validation they want to the Model as well.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top