开发者

How to validate data in C# interface fields?

开发者 https://www.devze.com 2023-02-12 14:03 出处:网络
I have a few classes that implement a certain interface. Is there any way, at the interface rather than implementing-class level, to define data validation rules?

I have a few classes that implement a certain interface.

Is there any way, at the interface rather than implementing-class level, to define data validation rules?

If not, what would be a suggested pattern t开发者_JS百科o factor out the data validation rules from specific classes? (EDIT: In my case, i'd like to avoid using an abstract base class to implement validation.)

Thank you


I would separate the validation logic into another class. For example, if your interface is IFoo you would have a FooValidator with a Validate(IFoo foo) method. This separates the implementation of IFoo from the business rules surrounding validation. The separation means that:

  • You use the same validator class for all implementations of IFoo
  • The validation logic doesn't depend on the specific implementation of IFoo
  • You can use different validators in different contexts, so an administrator could have less validation rules than a customer, or you could mock out the validation for automated tests

This example implementation uses an abstract ValidatorBase class that you wouldn't need to use initially, I prematurely optimised it :-$ .

interface IFoo
{
    int Age { get; }
    string Name { get; }
}
class Foo : IFoo
{
    public int Age { get; set; }
    public string Name { get; set; }
}

abstract class ValidatorBase<T>
{
    public class Rule    
    {
        public Func<T, bool> Test { get; set; }
        public string Message { get; set; }
    }

    protected abstract IEnumerable<Rule> Rules { get; }

    public IEnumerable<string> Validate(T t)
    {
        return this.Rules.Where(r => !r.Test(t)).Select(r => r.Message);
    }
}

class FooValidator : ValidatorBase<IFoo>
{
    protected override IEnumerable<ValidatorBase<IFoo>.Rule> Rules
    {
        get
        {
            return new Rule[] {
                new Rule { Test = new Func<IFoo,bool>(foo => foo.Age >= 0), Message = "Age must be greater than zero" },
                new Rule { Test = new Func<IFoo,bool>(foo => !string.IsNullOrEmpty(foo.Name)), Message = "Name must be provided" }
            };                    
        }
    }
}

static void Main(string[] args)
{
    var foos = new[] {
        new Foo { Name = "Ben", Age = 30 },
        new Foo { Age = -1 },
        new Foo { Name = "Dorian Grey", Age = -140 }
    };

    var fooValidator = new FooValidator();

    foreach (var foo in foos)
    {
        var messages = fooValidator.Validate(foo);
        if (!messages.Any()) Console.WriteLine("Valid");
        else foreach (var message in messages) Console.WriteLine("Invalid: " + message);
        Console.WriteLine();
    }
}

Running the program gives this result:

Valid

Invalid: Age must be greater than zero
Invalid: Name must be provided

Invalid: Age must be greater than zero


Maybe use abstract class as "man-in-the-middle" where you can place validation rules on properties?


Another possibility is to use attributes. You can create a custom attribute to define validation rules

public abstract class ValidationAttribute : Attribute
{
    public abstract bool IsValid(object value);
}

public class EvenValidation : ValidationAttribute
{
     public override bool IsValid(object value)
     {  
          if (!(value is int))
             return false;

          return ((int)value) % 2 == 0;
     }
}

public interface IFoo
{
     [EvenValidation]
     int SomeValue { get; }
}

public static class Validator
{
      public static bool IsValid(object component, object proposedValue, string property) 
      {
           //Use reflection to look for ValidationAttributes on the property
           //Use the ValidationAttribute to validate the proposed value
      }
}


Would it be possible to have these classes all derive from a common base class and implement your validation logic there? You could have the common base class implement the interface, and define abstract methods that the derived classes would implement to customize the behavior.


First you can not have any implementation in Interface itself.

If you have such requirement you are better off with an Abstract class.


You didn't mention if you were using WinForms, WPF, etc. and as other people say, you can't put this code into actual interfaces.

The suggested way is to implement IDataErrorInfo on a base class.

Here is an example of someone implementing this in a base class while using the MVVM pattern: IDataErrorInfo with MVVM

You do not have to follow MVVM, but you can follow his article that implements validation for WPF.

Even still, it is a little clunky as you have to do work in each of your properties. Which you can only get around by putting some of these properties into a common base class. There are more complicated ways around this if you are interested in trying something like Castle Windsor and setting up AOP, but I wouldn't recommend shoehorning it into a partially completed application.


You can not implement any code at interface level - so you cannot put validation login in interface.

You can add attributes to interface members if you have validation framework that reads attributes to perform validation.

To share validation code just have separate method that takes interface as parameter and do validation there like bool Validate(IMyInterface data){... return true;} .

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号