2011-11-07 28 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值)'。 –