每个应用程序都执行自己的验证。我想将这些功能引入到类库中,这样我们的开发人员就可以快速实现验证库,然后继续工作,而不是重新开发每个项目的轮子。
您的问题似乎与自定义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