2011-11-07 26 views
3

我在我的域中有几个应用程序接受文本字段中的类似输入。每个应用程序都实现自己的验证我想将这些功能引入到类库中,这样我们的开发人员就可以快速实现验证库,然后继续工作,而不是重新开发每个项目的轮子。我应该如何在C#中设计字符串验证类?

对于面向对象设计,我不是最好的。我需要的是用户输入任意字符串的能力,然后验证库根据已知类型检查它以确保它与其中一个匹配。我应该建立一个接口,并使每种类型的字符串都是一个实现该接口的类? (似乎是错误的,因为当我读取字符串时,我不知道该类型)。我可以使用一些帮助来确定一个模式。

谢谢。

+0

是否有可能使表单字段声明他们的类型是什么? –

+0

在我看来,这个决定完全取决于验证的复杂程度。如果可能,有时候开始简单是最好的方法。在许多情况下,带有验证方法的静态类,例如“ValidateZip(字符串值)”就足够了。你能详细说明吗? – harlam357

+0

开始简单的声音对我所做的事很好。我正在查看不同硬件模块的硬件标识符。我的用户知道他们是“序列号”,但实际上,他们可能是MAC地址,蜂窝部件的IMEI号码或我们的Web应用程序生成的唯一ID。我有为每种类型的ID定义的正则表达式语句,但我不确定如何有效地组织它们,以避免在成长过程中出现超长的正则表达式规则列表。 – GregB

回答

3

我一直是Fluent Validation for .Net.的粉丝如果它更健壮,那么你需要它,它的功能很容易模仿你自己。

如果你有兴趣,这是我的very simple validation class的链接。这与使用Fluent验证类似,但使用lambdas创建验证断言。以下是如何使用它的一个简单示例:

 
public class Person 
{ 
    public Person(int age){ Age = age; } 
    public int Age{ get; set;} 
} 

public class PersonValidator : AbstractValidator 
{ 
    public PersonValidator() 
    { 
     RuleFor(p => p.Age >= 0, 
      () => new ArgumentOutOfRangeException(
       "Age must be greater than or equal to zero." 
     )); 
    } 
} 

public class Example 
{ 
    void exampleUsage() 
    { 
     var john = new Person(28); 
     var jane = new Person(-29); 

     var personValidator = new PersonValidator(); 

     var johnsResult = personValidator.Validate(john); 
     var janesResult = personValidator.Validate(jane); 

     displayResult(johnsResult); 
     displayResult(janesResult); 
    } 

    void displayResult(ValidationResult result) 
    { 
     if(!result.IsValid) 
      Console.WriteLine("Is valid"); 
     else 
      Console.WriteLine(result.Exception.GetType()); 
    } 
} 

(有关更全面的示例,请参见source code)。

输出:

 
Is valid 
System.ArgumentOutOfRangeException 
+0

这似乎是一个非常棒的工具,但它并没有解决我的OO设计问题,这些规则应该放在哪里。 – GregB

+0

我正在借调@ErOx的答案,如果可以的话,请使用现有的库。如果你不能,我在过去使用正则表达式构建了库。 flyweight正则表达式对象的集合 - 可能是结构体 - 知道如何根据自己验证任意字符串可以给你一个候选匹配列表。注意处理多个匹配:确保您有一个啄食顺序,以确定哪个匹配是最匹配的。 – ssamuel

+0

我很想在C#中看到这些“轻量级正则表达式结构”。 –

0

你需要做到以下几点:

  1. 解析你的字符串,并计算出(在某种程度上)是什么类型的字符串。我希望在验证之前知道它(通过为字段分配类型),因为如果某个字符串不正确,则可以为其分配不正确的类型。
  2. 根据适用于给定字段类型的验证规则验证您的字符串。这些验证器应该实现一些接口,所以你可以验证任何类型的字符串。通常你不仅有字段类型特定的验证,而且还有字段特定的验证,所以这种验证器也应该实现相同的接口。

其他一切都取决于您的应用程序特定的逻辑。

2

每个应用程序都执行自己的验证。我想将这些功能引入到类库中,这样我们的开发人员就可以快速实现验证库,然后继续工作,而不是重新开发每个项目的轮子。

您的问题似乎与自定义NUnit约束类似。

NUnit允许他们称之为constraint-based assertion model的东西,并允许用户创建custom constraints,表示给定的对象是否满足该约束条件。

使用基于对象的约束模型优于纯粹基于函数的约束模型:

  • 它可以让你聚集亚约束来评估一个更高级别的约束。
  • 它允许您提供关于特定约束与您的输入数据不匹配的诊断信息。

这听起来很花哨,但如果没有约束只是把你所需类型的参数功能,返回true如果匹配,并false

它适应于您的问题

我需要的是一个用户输入任意字符串的能力,进而为验证库,以检查它与已知的类型,以确保它匹配其中之一。

您实际上并不需要从约束中构建断言。您可以在不抛出异常的情况下评估约束,并首先进行分类。

但我不建议你做任何自动分类。我建议你给特定的输入附加一个特定的约束,而不是试图匹配所有可用的约束。将string传递给该约束,并将其称为完成。

如果您需要为较高级别的对象执行此操作,请为使用特定(现有)约束的较高级别对象为其每个子字段构建约束,以及执行跨字段约束验证。

完成后,您可以将所有约束违规集合到顶层,并让您的验证逻辑抛出包含所有违规的异常。

BTW,我不会用完全相同的接口NUnit的作用:

  • 这是一个令人困惑的设计
  • 我要选择一个使用泛型通过
  • 我一路的方法ð宁愿允许你返回IEnumerable<ConstraintViolation>IEnumerable<string>,而不是采取某种形式的输出作家班作为一个依赖

的做法,但我肯定会STE人基本概念:)

实施

这里就是我在谈论的一个示例实现:

public class ConstraintViolation 
{ 
    public ConstraintViolation(IConstraintBase source, string description) 
    { 
     Source = source; 
     Description = description; 
    } 

    public IConstraintBase Source { get; } 
    public string Description { get; set; } 
} 

public interface IConstraintBase 
{ 
    public string Name { get; } 
    public string Description { get; } 
} 

public interface IConstraint<T> : IConstraintBase 
{ 
    public IEnumerable<ConstraintViolation> GetViolations(T value); 
} 

这里是一个例子约束来验证字符串的长度(弱示例,但看到我下面的评论):

public class StringLengthConstraint : IConstraint<string> 
{ 
    public StringLengthConstraint(int maximumLength) 
     : this(minimumLength: 0, maximumLength: maximumLength) 
    { 
    } 

    public StringLengthConstraint(int minimumLength, int maximumLength, 
     bool isNullAllowed = false) 
    { 
     MinimumLength = minimumLength; 
     MaximumLength = maximumLength; 
     IsNullAllowed = isNullAllowed; 
    } 

    public int MinimumLength { get; private set; } 
    public int MaximumLength { get; private set; } 
    public bool IsNullAllowed { get; private set; } 

    public IEnumerable<ConstraintViolation> GetViolations(string value) 
    { 
     if (value == null) 
     { 
      if (!IsNullAllowed) 
      { 
       yield return CreateViolation("Value cannot be null"); 
      } 
     } 
     else 
     { 
      int length = value.Length; 

      if (length < MinimumLength) 
      { 
       yield return CreateViolation(
        "Value is shorter than minimum length {0}", 
        MinimumLength); 
      } 

      if (length > MaximumLength) 
      { 
       yield return CreateViolation("Value is longer than maximum length {0}", 
        MaximumLength); 
      } 
     } 
    } 

    public string Name 
    { 
     get { return "String Length"; } 
    } 

    public string Description 
    { 
     get 
     { 
      return string.Format("Ensure a string is an acceptable length" 
       + " - Minimum: {0}" 
       + ", Maximum: {1}" 
       + "{2}" 
       , MinimumLength 
       , MaximumLength 
       , IsNullAllowed ? "" : ", and is not null" 
       ); 
     } 
    } 

    private ConstraintViolation CreateViolation(string description, 
     params object[] args) 
    { 
     return new ConstraintViolation(this, string.Format(description, args)); 
    } 
} 

下面是如何使用它当做validati在单场:

var violations = new StringLengthConstraint(10).GetViolations(value); 

if(violations.Any()) 
{ 
    throw new InvalidArgumentException("value", string.Join(", ", violations)); 
} 

理由

字符串长度的限制是大量的代码做一些愚蠢的简单,特别是如果你只是一次这样做。但也有优点,这种方法:

它是可重复使用的

写这篇文章,或者一旦使用它,而且我同意这是一种痛苦。

但是这里的大部分代码都是为了让它可以重用。例如,您可以从string类型的约束列表中选择此项。或者您可以在工具提示等界面上显示约束或约束违规列表,或者您可以在单元测试框架中使用它;有了适配器类,它可以直接插入到NUnit中。

该模型支持聚集约束和违反

  • 通过LINQ的
  • 通过对象组合物

的LINQ:

var violations = new SomeConstraint(someData).GetViolations(value) 
    .Concat(new SomeOtherConstraint(someData).GetViolations(value)) 
    ; 

对象组成:

// ... 
public IEnumerable<ConstraintViolation> GetViolations(SomeType value) 
{ 
    if(value == 42) 
    { 
     yield return new ConstraintViolation(this, "Value cannot be 42"); 
    } 

    foreach(var subViolation in subConstraint.GetViolations(value)) 
    { 
     yield return subViolation; 
    } 
} 

private SomeSubConstraint subConstraint; 
+0

如果你发现自己做了比验证更多的匹配(强制规定没有违规存在),那么你可以添加一个'bool IsSatisfiedBy(T值)'。 –