wpf - 实体/业务对象验证的建议,其中验证依赖于其他实体/服务

标签 wpf silverlight validation mvvm entity

上下文

对于使用 MVVM 模式的 WPF 应用程序,我使用实体上的 IDataErrorInfo 接口(interface)验证我的实体(/业务对象),以便 WPF 自动调用我的实体中的验证规则,并且验证错误自动出现在 View 中。 (受本文中 Josh Smith 的启发:http://joshsmithonwpf.wordpress.com/2008/11/14/using-a-viewmodel-to-provide-meaningful-validation-error-messages/

这适用于简单的验证规则,例如(名称 > 10 个字符,值必须 > 0)

但是当模型中的验证规则更复杂时该怎么办(比如名称必须是唯一的/属性的最大值在另一个实体中定义)。我首先想到通过让实体引用存储库来解决这个问题,但这感觉不好,因为我认为应该只有从存储库到实体的引用而不是其他方式(创建循环引用)

从配方实体引用 ConfigurationRepository 是否“合法”。或者你有更好的建议吗?
您是否有建议如何实现实体/业务对象验证,其中验证依赖于其他实体/服务,如下例所示。

下面是我现实世界问题的简化代码。
在配方实体中,我想验证最高温度是否小于存储在 Configuration.MaximumTemperature 中的值。 你会如何解决这个问题?

配置实体 (存储配方的最高允许温度)

public class Configuration: INotifyPropertyChanged, IDataErrorInfo
{
    private int _MaxTemperatureSetpoint;
    public int MaxTemperatureSetpoint
    {
        get { return _MaxTemperatureSetpoint; }
        set
        {
            if (value != _MaxTemperatureSetpoint)
            {
                _Setpoint = value;
                RaisePropertyChanged("MaxTemperatureSetpoint");
            }
        }
    }

简化配方 (用户配置具有所需温度 (TemperatureSetpoint) 和所需时间 (TimeMilliSeconds) 的配方的类。TemperatureSetpoint 必须小于 Configuration.MaxTemperature)
public class Recipe: INotifyPropertyChanged, IDataErrorInfo
{
    private int _TemperatureSetpoint;
    public int TemperatureSetpoint
    {
        get { return _TemperatureSetpoint; }
        set
        {
            if (value != _TemperatureSetpoint)
            {
                _Setpoint = value;
                RaisePropertyChanged("Setpoint");
            }
        }
    }

    private int _TimeMilliSeconds;
    public int TimeMilliSeconds
    {
        get { return _TimeMilliSeconds; }
        set
        {
            if (value != _TimeMilliSeconds)
            {
                _TimeMilliSeconds= value;
                RaisePropertyChanged("TimeMilliSeconds");
            }
        }
    }

   #region IDataErrorInfo Members
    public string Error
    {
        get { throw new NotImplementedException(); }
    }

    public string this[string propertyName]
    {
        get 
        { 
             switch(propertyName)
             {
                 case "TimeMilliSeconds":
                     //TimeMilliSeconds must be < 30 seconds
                     if (TimeMilliSeconds < 30000)
                     { return "TimeMilliSeconds must be > 0 milliseconds";}
                 case "TemperatureSetpoint":

                    //MaxTemperatureSetpoint < maxTemperature stored in the ConfigurationRepository

                    int maxTemperatureSetpoint = ConfigurationRepository.GetConfiguration().MaxTemperatureSetpoint;
                     if (TemperatureSetpoint> maxTemperatureSetpoint )
                     { return "TemperatureSetpoint must be < " + maxTemperatureSetpoint.ToString();}
       }
    }

    #endregion
}

配方库
public interface IRecipeRepository
{
    /// <summary>
    /// Returns the Recipe with the specified key(s) or <code>null</code> when not found
    /// </summary>
    /// <param name="recipeId"></param>
    /// <returns></returns>
    TemperatureRecipe Get(int recipeId);

    .. Create + Update + Delete methods
}

配置库
public interface IConfigurationRepository
{
      void Configuration GetConfiguration();
}

最佳答案

对于基于业务规则的验证,我通常会公开一个我的 ViewModel 可以设置的验证委托(delegate)。

例如,Recipe 的 ViewModel 可能包含如下代码:

public GetRecipe(id)
{
    CurrentRecipe = DAL.GetRecipe(id);
    CurrentRecipe.AddValidationErrorDelegate(ValidateRecipe);
}

private string ValidateRecipe(string propertyName)
{
    if (propertyName == "TemperatureSetpoint")
    {
        var maxTemp = Configuration.MaxTemperatureSetpoint;
        if (CurrentRecipe.TemperatureSetpoint >= maxTemp )
        {
            return string.Format("Temperature cannot be greater than {0}", maxTemp);
        }
    }
    return null;
}

这个想法是你的Model应该只包含原始数据,因此它应该只验证原始数据。这可以包括验证最大长度、必填字段和允许的字符等内容。包含业务规则的业务逻辑应在 ViewModel 中进行验证。 ,这允许这种情况发生。

我的IDataErrorInfo的实际实现在 Recipe类看起来像这样:
#region IDataErrorInfo & Validation Members

/// <summary>
/// List of Property Names that should be validated
/// </summary>
protected List<string> ValidatedProperties = new List<string>();

#region Validation Delegate

public delegate string ValidationErrorDelegate(string propertyName);

private List<ValidationErrorDelegate> _validationDelegates = new List<ValidationErrorDelegate>();

public void AddValidationErrorDelegate(ValidationErrorDelegate func)
{
    _validationDelegates.Add(func);
}

#endregion // Validation Delegate

#region IDataErrorInfo for binding errors

string IDataErrorInfo.Error { get { return null; } }

string IDataErrorInfo.this[string propertyName]
{
    get { return this.GetValidationError(propertyName); }
}

public string GetValidationError(string propertyName)
{
    // If user specified properties to validate, check to see if this one exists in the list
    if (ValidatedProperties.IndexOf(propertyName) < 0)
    {
        //Debug.Fail("Unexpected property being validated on " + this.GetType().ToString() + ": " + propertyName);
        return null;
    }

    string s = null;

    // If user specified a Validation method to use, Validate property
    if (_validationDelegates.Count > 0)
    {
        foreach (ValidationErrorDelegate func in _validationDelegates)
        {
            s = func(propertyName);
            if (s != null)
            {
                return s;
            }
        }
    }

    return s;
}

#endregion // IDataErrorInfo for binding errors

#region IsValid Property

public bool IsValid
{
    get
    {
        return (GetValidationError() == null);
    }
}

public string GetValidationError()
{
    string error = null;

    if (ValidatedProperties != null)
    {
        foreach (string s in ValidatedProperties)
        {
            error = GetValidationError(s);
            if (error != null)
            {
                return error;
            }
        }
    }

    return error;
}

#endregion // IsValid Property

#endregion // IDataErrorInfo & Validation Members

关于wpf - 实体/业务对象验证的建议,其中验证依赖于其他实体/服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8011946/

相关文章:

silverlight - 如果下载成功,为什么 WebClient.OpenReadAsync 会返回长度为零的 e.Result?

validation - 是否可以检查 Blazor ValidationMessageStore 是否有任何错误消息

JAVA api验证/异常处理

c# - 绑定(bind)到 xpath 时 Mediaelement repeatbehavior 失败

c# - 为什么TextBlock不是路由事件上的OriginalSource?

wpf - 如何从另一个 ".cs"文件中获取 DataGrid.SelectedIndex?(MVVM)

c# - 如何在 XAML 中换行或换行

c# - MVVM 异常处理

.net - Windows Mobile 6.5 上的 Windows Phone 7 应用程序

python - 在 Python 中验证用户输入的字符串