如何实现仅在运行时期间已知的验证规则

时间:2015-07-20 10:20:20

标签: c# validation


我有以下情况 我有一堆简单的类,例如这个

public class Student
{
    public int Id { get; set; }
    public int Age { get; set; }
    public decimal AverageMark { get; set; }
    public string Name { get; set; }
    public string University { get; set; }
}

每个用户都可以创建,编辑和删除的网页。当我们创建更新学生时,我们需要对其进行验证。 问题是我们在编译期间不知道验证规则!!!
我们为管理员设置了单独的网页,他设置了验证标准,  例如,学生年龄不能低于15或大学必须等于“SomeUniversity”。
结果我有一些存储在我的数据库中的标准列表

public class Criteria
{
    public string PropertyName { get; set; }
    public string OperationName { get; set; }
    public string OperationValue { get; set; }
}

我为调查目的创建了简单的控制台应用程序。这是代码

namespace DynamicValidation
{
class Program
{
    static void Main(string[] args)
    {
        //set up students
        var student1 = new Student() { Age = 20, AverageMark = 4, Name = "Ihor", University = "Lviv National University" };
        var student2 = new Student() { Age = 20, AverageMark = 4, Name = "Taras", University = "Lviv National University" };
        var student3 = new Student() { Age = 20, AverageMark = 5, Name = "Marko", University = "" };
        var student4 = new Student() { Age = 20, AverageMark = 3, Name = "Tanya", University = "" };
        var student5 = new Student() { Age = 22, AverageMark = 4, Name = "Ira", University = "" };


        var students = new List<Student>() { student1, student2, student3, student4, student5 };

        //set up validation rules
        var criteria1 = new Criteria("Age", "Equal", "20");
        var criteria2 = new Criteria("AverageMark", "NotLessThan", "4");
        var criteria3 = new Criteria("University", "Contains", "Lviv");

        var criterias = new List<Criteria>() { criteria1, criteria2, criteria3 };

        var result = new List<Student>();
        foreach (var currentStudent in students)
        {
            foreach (var currentCriteria in criterias)
            {
                object currentPropertyValue = typeof(Student).GetProperty(currentCriteria.PropertyName).GetValue(currentStudent);

                //what is next ???!!!
            }
        }
    }
}

public class Student
{
    public int Id { get; set; }
    public int Age { get; set; }
    public decimal AverageMark { get; set; }
    public string Name { get; set; }
    public string University { get; set; }
}

public class Criteria
{
    public string PropertyName { get; set; }
    public string OperationName { get; set; }
    public string OperationValue { get; set; }
}

}

我如何实现这段代码? (表达树,动态吗?)
我不希望你为我工作但也许有一些关于这个的文章? (我试图找到但没有成功)
也许有一些关于方法的建议? 也许有一些类似的开放代码?
或者它可能已经在一些图书馆中实施了?
将感谢任何帮助:)

2 个答案:

答案 0 :(得分:2)

您可以编写学生验证函数,请参阅IsValidStudent(Criteria criteria)

public class Student
{
    public int Id { get; set; }
    public int Age { get; set; }
    public decimal AverageMark { get; set; }
    public string Name { get; set; }
    public string University { get; set; }

    public bool IsValidStudent(Criteria criteria)
    {
        return IsValidByAge(criteria) 
            && IsValidByMarks(criteria) 
            && IsValidByUniversity(criteria);
    }

    private bool IsValidByAge(Criteria criteria)
    {
        switch (criteria.OperationType)
        {
            case Criteria.Operation.GreaterThan:
                return Convert.ToInt32(criteria.OperationValue) > this.Age;
            case Criteria.Operation.LessThan:
                return Convert.ToInt32(criteria.OperationValue) < this.Age;
            case Criteria.Operation.EqualTo:
                return Convert.ToInt32(criteria.OperationValue) == this.Age;
            default:
                return false;
        }
    }

    private bool IsValidByMarks(Criteria criteria)
    {
        // etc...
    }

    private bool IsValidByUniversity(Criteria criteria)
    {
        // etc...
    }
}

用法:

var result = new List<Student>();
foreach (var currentStudent in students)
{
     foreach (var currentCriteria in criterias)
     {
           if (currentStudent.IsValidStudent(currentCriteria))
           {
               result.Add(currentStudent);
           }
     }
}

我还扩展了你的Criteria课程:

public class Criteria
{
    public string PropertyName { get; set; }
    public Operation OperationType { get; set; }
    public string OperationValue { get; set; }

    public enum Operation
    {
        EqualTo,
        GreaterThan,
        LessThan,
        Contains
    }

    public Criteria(string propertyName, Operation operationType, string operationValue)
    {
        this.PropertyName = propertyName;
        this.OperationType = operationType;
        this.OperationValue = operationValue;
    }
} 

答案 1 :(得分:0)

恕我直言,我找到了一个更好的解决方案,然后提出了 现在,如果要添加新属性,我们不需要更改Student类中的验证逻辑。此代码也可以应用于任何其他类(不仅适用于以前的Student类)

验证界面

public interface IValidator
{
    bool Validate(object value, object validateWith);
}

一组实施

public class ContainsValidator : IValidator
{
    public bool Validate(object value, object validateWith)
    {
        string valueString = Convert.ToString(value);
        string validateWithString = Convert.ToString(validateWith);

        return valueString.Contains(validateWithString);
    }
}

public class StartWithValidator : IValidator
{
    public bool Validate(object value, object validateWith)
    {
        string valueString = Convert.ToString(value);
        string validateWithString = Convert.ToString(validateWith);

        return valueString.StartsWith(validateWithString);
    }
}

public class LengthValidator : IValidator
{
    public bool Validate(object value, object validateWith)
    {
        string valueString = Convert.ToString(value);
        int valueLength = Convert.ToInt32(validateWith);

        return (valueString.Length == valueLength);
    }
}

public class LessThanValidator : IValidator
{
    public bool Validate(object value, object validateWith)
    {
        decimal valueDecimal = Convert.ToDecimal(value);
        decimal validateWithDecimal = Convert.ToDecimal(validateWith);

        return (valueDecimal < validateWithDecimal);
    }
}

public class MoreThanValidator : IValidator
{
    public bool Validate(object value, object validateWith)
    {
        decimal valueDecimal = Convert.ToDecimal(value);
        decimal validateWithDecimal = Convert.ToDecimal(validateWith);

        return (valueDecimal > validateWithDecimal);
    }
}

public class EqualValidator : IValidator
{
    public bool Validate(object value, object validateWith)
    {
        string valueString = Convert.ToString(value);
        string validateWithString = Convert.ToString(validateWith);

        return (valueString == validateWithString);
    }
}

和用法

class Program
{
    static void Main(string[] args)
    {
        //set up students
        var student1 = new Student() { Age = 20, AverageMark = 5, Name = "Ihor", University = "Lviv National University" };
        var student2 = new Student() { Age = 20, AverageMark = 5, Name = "SomeLongName", University = "Lviv National University" };
        var student3 = new Student() { Age = 20, AverageMark = 5, Name = "Taras", University = "Kyiv National University" };
        var student4 = new Student() { Age = 20, AverageMark = 5, Name = "Marko", University = "Some University" };
        var student5 = new Student() { Age = 20, AverageMark = 4, Name = "Tanya", University = "Lviv National University" };
        var student6 = new Student() { Age = 22, AverageMark = 4, Name = "Ira", University = "" };

        var students = new List<Student>() { student1, student2, student3, student4, student5, student6 };

        //set up validation rules
        var criteria1 = new Criteria("Age", "Equal", "20");
        var criteria2 = new Criteria("AverageMark", "MoreThen", "4");
        var criteria3 = new Criteria("University", "Contains", "National");
        var criteria4 = new Criteria("University", "StartWith", "Lviv");
        var criteria5 = new Criteria("Name", "Length", "4");

        var criterias = new List<Criteria>() { criteria1, criteria2, criteria3, criteria4, criteria5 };

        var result = new List<Student>();
        foreach (var currentStudent in students)
        {
            var isValid = true;
            foreach (var currentCriteria in criterias)
            {
                object currentPropertyValue = typeof(Student).GetProperty(currentCriteria.PropertyName).GetValue(currentStudent);
                IValidator currentValidator = ValidatorFactory.GetValidator(currentCriteria.OperationName);

                bool validationResult = currentValidator.Validate(currentPropertyValue, currentCriteria.OperationValue);
                if (!validationResult)
                {
                    isValid = false;
                    break;
                }
            }

            if (isValid)
                result.Add(currentStudent);
        }
    }
}

最后是ValidatorFactory的代码

public class ValidatorFactory
{
    public static IValidator GetValidator(string validatorName)
    {
        validatorName = validatorName.ToUpper();
        switch (validatorName)
        {
            case "CONTAINS": return new ContainsValidator();
            case "STARTWITH": return new StartWithValidator();
            case "EQUAL": return new EqualValidator();
            case "MORETHEN": return new MoreThanValidator();
            case "LENGTH": return new LengthValidator();
            default: throw new Exception("There are not appropriate validator.");
        }
    }
}

也许这将有助于未来的人:)