每個應用程序都執行自己的驗證。我想將這些功能引入到類庫中,這樣我們的開發人員就可以快速實現驗證庫,然後繼續工作,而不是重新開發每個項目的輪子。
您的問題似乎與自定義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:
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;
是否有可能使表單字段聲明他們的類型是什麼? –
在我看來,這個決定完全取決於驗證的複雜程度。如果可能,有時候開始簡單是最好的方法。在許多情況下,帶有驗證方法的靜態類,例如「ValidateZip(字符串值)」就足夠了。你能詳細說明嗎? – harlam357
開始簡單的聲音對我所做的事很好。我正在查看不同硬件模塊的硬件標識符。我的用戶知道他們是「序列號」,但實際上,他們可能是MAC地址,蜂窩部件的IMEI號碼或我們的Web應用程序生成的唯一ID。我有爲每種類型的ID定義的正則表達式語句,但我不確定如何有效地組織它們,以避免在成長過程中出現超長的正則表達式規則列表。 – GregB