从Validate函数返回额外信息的最佳实践

时间:2009-06-25 12:11:57

标签: c#

我有一个班级员工。我希望能够在保存之前验证它()以确保所有字段都填充了有效值。 类的用户可以在调用Save()之前调用Validate(),或者他们可以直接调用Save(),然后Save()将调用Validate(),如果验证失败,可能会抛出异常。

现在,我的(主要)问题是这个;
如果我的Validate()函数返回一个简单的bool,那么如何告诉用户该类的错误,即“未填写电子邮件”,“ID不唯一”等。出于此目的,我只想要错误字符串传递给人类用户,但如果我想要一个错误代码列表(除了使位图的使用更符合逻辑),原则是相同的。

  • 我可以在验证函数中使用Out参数,但我知道这是不赞成的。
  • 我可以从我的函数中返回一个字符串数组,而不是返回一个bool,只测试它是否为空(意味着没有错误) - 但这看起来很麻烦而且不对。
  • 我可以创建一个仅仅从这个方法返回的Struct,包括一个bool和一个包含错误消息的字符串数组,但看起来很笨拙。
  • 我可以返回错误代码的位图而不是bool并查找它,但这似乎相当过分。
  • 我可以在对象上创建一个公共属性“ValidationErrors”来保存错误。但是,这将依赖于我在读取之前调用Validate()或从Property()显式调用Validate,这有点浪费。

我的特定程序是在C#中,但这看起来像是一个相当通用的“最佳实践”问题,我相信我应该知道答案。感谢任何建议。

8 个答案:

答案 0 :(得分:6)

  

我可以创建一个仅用于从此方法返回的Struct,包括bool和带有错误消息的字符串数组,但看起来很笨拙。

为什么看起来很笨重?创建适当的类型来封装信息是完美的。但是,我不一定会使用字符串来编码这些信息。枚举可能更适合。

另一种方法是对返回类型进行子类化,并为每种情况提供额外的子类 - 如果这是合适的话。如果可能发出多个故障信号,则表示阵列正常。但我也会将其封装在一个自己的类型中。

一般模式可能如下所示:

class ValidationInfo {
    public bool Valid { get; private set; }
    public IEnumerable<Failure> Failures { get; private set; }
}

答案 1 :(得分:5)

我可能会选择bitmap-option。简单

[Flags]
public enum ValidationError {
    None = 0,
    SomeError = 1,
    OtherError = 2,
    ThirdError = 4
}

...在调用代码中,只需:

ValidationError errCode = employee.Validate();
if(errCode != ValidationError.None) {
    // Do something
}

对我而言似乎很好,也很紧凑。

答案 2 :(得分:2)

听起来你需要一个通用类:

public sealed class ValidationResult<T>
{
    private readonly bool _valid; // could do an enum {Invalid, Warning, Valid}
    private readonly T _result;
    private readonly List<ValidationMessage> _messages;

    public ValidationResult(T result) { _valid = true; _result = result; _messages = /* empty list */; }

    public static ValidationResult<T> Error(IEnumerable<ValidationMessage> messages)
    {
        _valid = false;
        _result = default(T);
        _messages = messages.ToList();
    }

    public bool IsValid { get { return _valid; } }
    public T Result { get { if(!_valid) throw new InvalidOperationException(); return _result; } }
    public IEnumerable<ValidationMessage> Messages { get { return _messages; } } // or ReadOnlyCollection<ValidationMessage> might be better return type

    // desirable things: implicit conversion from T
    // an overload for the Error factory method that takes params ValidationMessage[]
    // whatever other goodies you want
    // DataContract, Serializable attributes to make this go over the wire
}

答案 3 :(得分:1)

我会遵循TryParse方法的模式并使用带有此签名的方法:

public bool TryValidate(out IEnumerable<string> errors) { ... }

另一种选择是将验证码从对象中拉出到自己的类中,可能建立在规范模式上。

public class EmployeeValidator
{
    public bool IsSatisfiedBy(Employee candidate) 
    { 
        //validate and populate Errors 
    }
    public IEnumerable<string> Errors { get; private set; }
}

答案 4 :(得分:1)

我发现这是一个很好的方法,只需要一个方法(或一个属性,因为C#有很好的支持),它以某种明智,易于使用的格式返回所有验证错误消息,例如列表字符串。

这样你也可以保持你的验证方法回归bools。

答案 5 :(得分:1)

您可以查看Rockford Lhotka的CSLA,其中包含广泛的业务规则/验证跟踪业务对象。

www.lhotka.net

答案 6 :(得分:1)

我同意Chris W.在阅读Rocky的专家C#Business Objects之前,我问了同样的问题。

他有一种处理业务验证规则的出色方法。在设置每个属性后进行验证。每当规则被破坏时,对象的状态变为InValid。

您的业务类可以实现IDataError接口。将UI控件绑定到业务对象属性后,将通知ErrorProvider控件对象上的任何损坏规则。

我真的建议你花点时间看一下验证部分。

答案 7 :(得分:0)

我们正在使用Spring验证和Windows窗体错误提供程序。

因此,我们的验证函数返回一个带有控件ID和错误消息的字典(对于每个验证错误)。错误提供程序在控件附近的弹出字段中显示错误消息,导致错误。

我过去曾使用过其他一些验证方案 - 但这个方案效果很好。