19

I'm interested in setting up client side validation using a WinForms application and Entity Framework 5. I understand that there's the IValidatableObject interface that I can implement to perform and custom validation that I may need for each entity.

However, since I'm using WinForms I'd like to use the ErrorProvider to present the user with a nice notification when there is a validation error as they fill out a form. Is this functionality able to be achieved using the IValidatableObject interface or would I need to implement the IDataErrorInfo interface on my entities as well in order to have the ErrorProvider work properly?

If you have any other suggestions on a better alternative to this please let me know and I'll gladly look into that as well.

3
  • the OP has asked for EF 5, but hopefully it works for EF 4.1 as well! Commented Dec 1, 2013 at 9:30
  • It does work for ef 4.1 Commented Dec 3, 2013 at 0:45
  • You might want to try what I've suggested here. This might only apply though if you're using code first. You could just attach it to run on some user input event. stackoverflow.com/a/19170277/2592994 Commented Dec 7, 2013 at 5:03

2 Answers 2

10
+100

Lets say you have an Entity called Car and this class contains an property which need be validated.

public class Car
{
  [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public int Id { get; set; }

  // Accepted values have to be between 1 and 5.
  public int NeedToBeValidatedRange { get; set; }
}

You have to create a base class for all your entites in my example I will called Entity.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;

/// This is the base class for all entities and it provide a change notfication.
public abstract class Entity : INotifyPropertyChanged
{
  // Event fired when the property is changed!
  public event PropertyChangedEventHandler PropertyChanged;


  /// Called when int property in the inherited class is changed for ther others properties like (double, long, or other entities etc,) You have to do it.
  protected void HandlePropertyChange(ref int value, int newValue, string propertyName)
  {
    if (value != newValue)
    {
      value = newValue;
      this.Validate(propertyName);
      this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }

  /// Validate the property 
  /// <returns>
  /// The list of validation errors
  /// </returns>
  private ICollection<ValidationResult> PropertyValidator(string propertyName)
  {
    var validationResults = new Collection<ValidationResult>();
    PropertyDescriptor property = TypeDescriptor.GetProperties(this)[propertyName];

    Validator.TryValidateProperty(
      property.GetValue(this),
      new ValidationContext(this, null, null) { MemberName = propertyName },
      validationResults);

    return validationResults;
  }

  /// Validates the given property and return all found validation errors.
  private void Validate(string propName)
  {
    var validationResults = this.PropertyValidator(propName);
    if (validationResults.Count > 0)
    {
      var validationExceptions = validationResults.Select(validationResult => new ValidationException(validationResult.ErrorMessage));
      var aggregateException = new AggregateException(validationExceptions);
      throw aggregateException;
    }
  }
}

Now you shall modfiy the Car class and it should be like that:

public class Car : Entity
{
  private int id;
  private int needToBeValidatedRange;

  [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public int Id
  {
    get
    {
      return this.id;
    }
    set
    {
      this.HandlePropertyChange(ref this.id, value, "Id");
    }
  }

  [Range(1, 5)]
  public int NeedToBeValidatedRange
  {
    get
    {
      return this.needToBeValidatedRange;
    }
    set
    {
      this.HandlePropertyChange(ref this.needToBeValidatedRange, value, "NeedToBeValidatedRange ");
    }
  }
}

Somewhere in the user interface you are creating the car entities:

Car car1 = new Car();
car1.NeedToBeValidatedRange = 3;  // This will work!

Car car2 = new Car();
car2.NeedToBeValidatedRange = 6;  // This will throw ValidationException
  • WPF support very good ValidationException.
  • Winforms support partially ValidationException but now you are free how to handle this.
Sign up to request clarification or add additional context in comments.

6 Comments

How can validator check the new value if the order is this.Validate(propertyName); value = newValue; ? You either need to pass the value to the validator along with propname, or you need to first set, then validate, then rollback on error.
Yes exactly you have to put the value and then validate I will change the code order thanks.
Also, this approach only allows you to validate properties piece-wise. You will not be able to handle complex multifield validation, since attempt to change first property involved will cause a failure since other N properties are not changed yet (and cannot be since first prop threw error). This can be solved with i.e. some IEditXXX or IBeginXX/IEndXXX interfaces, but will add much complexity. I don't say the approach you provided is wrong - it is great/ok in simple cases. I write just as a warning, be careful to not end in a dead-end at some point of time.
@quetzalcoatl You can validated the whole entity also we have applied this approach in very complex class structure. I can update it but I want just to keep it at first very simple to him as prototype.
My point is that change-wise validation performed at each change to a property is not sufficient or correct or a good way in some cases. BTW. In case we misunderstood each other: by multi-field validation I mean i.e. validating that "if X is true then (Y=Z+A and Y>0) else Y=0". Trying to validate X,Y,Z,A change-wise would be quite complex. If you managed to solve it - no worries, that's just great. But certainly it could be done simpler in another way. ;)
|
5

There are two choices:

  • extend your poco classes with IValidateObject and IdataErrorInfo and raise the ui error in the validation method.
  • catch the validation error when save changes is called and invoke ErrorProvider directly depending on which entity field generates the validation error.

See the following for examples of extending poco classes with IValidateObject and handling validation errors when save changes are called.

http://msdn.microsoft.com/en-us/data/gg193959.aspx

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.