kro*_*mon 6 c# nhibernate validation wpf mvvm
我正在使用MVVM模式构建一个C#WPF应用程序.我有使用NHibernate来保存我的域模型的存储库类.
我的模型由一个更大的树结构组成(Recipe
包含Operation
s的Phase
s).操作和阶段都包含一个动态的键值映射列表IDictionary<string, string>
.对应的NHibernate映射Operation
是
<class name="Operation" table="operations">
<id column="id" type="int" generator="native" />
<property name="Name" column="name" />
<map name="Parameters" table="operation_params">
<key column="operation" />
<index column="param" type="string" />
<element column="value" type="string" />
</map>
<list name="Phases" cascade="all-delete-orphan">
<key column="operation" />
<index column="`index`" />
<one-to-many class="Phase" />
</list>
</class>
Run Code Online (Sandbox Code Playgroud)
现在,这部分很简单,效果很好.的Operation
类目前是一个POCO几乎没有逻辑的内部,一个简单的数据容器.
我的问题是:我必须根据我的应用程序从.xml文件中读取的外部模式验证参数.模式包含对单个参数(范围,有效值等)的限制以及几个参数之间的依赖关系(即,有效值根据另一个参数的值而变化).
集成验证逻辑的最佳方法是什么?我在最近几天看了很多,直到现在,我偶然发现了以下几种选择:
将验证逻辑添加到模型类本身.
为此,我不知道如何将验证模式正确地注入到NHibernate创建的对象中.我不需要一直使用验证功能,只有当用户正在编辑参数或我正在导入操作时(例如从备份中).那么也许我可以在模型类中实现实际的验证逻辑,并在我真正需要验证时使用属性注入验证规则?将该功能添加到我使用NHibernate存储的模型类中,或者模型类是否保持"哑",这是一种良好的做法吗?
为包装我的Operation
对象的验证逻辑创建一个装饰器类.
这样我每次需要验证时都会使用包装器,而只需要显示它就可以使用裸模型类.我的问题是我的ViewModel类已经是包装器,所以我会在这里得到另一层包装.此外,由于Operation类是较大树结构(配方/操作/阶段)的一部分,我需要为集合创建包装器,并将集合更改映射回底层集合,这可能是一项复杂的任务.
创建一个服务类,每当我想验证它时,我都会调用该服务.
我在这里看到的问题是该服务是无状态的,因此每次用户更改单个参数时都必须重新验证整个参数列表.这似乎不是最好的方法,特别是当我想在参数的验证状态发生变化时为UI触发某种更改事件时.
我的问题的常见方法是什么?是否有一种我还没有找到的模式,这完全是我需要的?我的意思是,有很多实现依赖于外部模式定义进行验证(阅读:XML/XSD和类似的文档结构),只需要有一些天才已经找到了解决我问题的完美解决方案;-)帮帮我吧!
我赞成以下解决方案:
将验证器项目添加到包含以下内容的解决方案:
根据应用程序从.xml文件读取的外部模式验证参数的逻辑.
您在项目中使用的每个POCO对象的验证规则,它需要验证(或者,您也可以在更高级别应用这些规则,如果您已经有这样的实现,则意味着不是POCO而是POCO上的一些Wrapper,但是尝试将规则直接应用于POCO - 更清洁和正确的方法
所以:
1-您的POCO将包含属性和简单验证SelfValidate():
namespace Core.Domain {
public class Operation : ValidatableDomainObject {
#region Properties
public virtual String Name { get; set; }
public virtual ISet Phases { get; set; }
#endregion Properties
#region Validation
public override ValidationResult SelfValidate() {
return ValidationHelper.Validate(this);
}
#endregion Validation
}
}
2-您的POCO验证器将包含应根据您的XML文件应用于验证POCO的规则:
#region Usings
using System.Linq;
using FluentValidation;
using FluentValidation.Results;
#endregion Usings
namespace Core.Validation {
public class OperationValidator : AbstractValidator {
#region .Ctors
///
/// .Ctor used for operation purpose
///
public OperationValidator() {
Validate();
}
#endregion .Ctors
///
/// Validation rules for Operation
///
private void Validate() {
//here you may get validations rules from you xml file and structure the following code after your requirements
//Name
RuleFor(x => x.Name).Length(2, 20).WithMessage("Operation name should have length between 2 and 20 symbols");
//ApplicationFormsWrapper
Custom(entity => {
foreach (var item in entity.Phases)
if (item.PhaseState == null)
return new ValidationFailure("Phases", "First Phase is missing");
return null;
});
}
}
}
3-添加ValidatableDomainObject类,它实现System.ComponentModel.IDataErrorInfo(提供用于提供用户界面可绑定的自定义错误信息的功能):
#region Usings
using System.ComponentModel;
using System.Linq;
using FluentValidation.Results;
using Core.Validation.Helpers;
#endregion Usings
namespace Core.Domain.Base {
public abstract class ValidatableDomainObject : DomainObject, IDataErrorInfo {
public abstract ValidationResult SelfValidate();
public bool IsValid {
get { return SelfValidate().IsValid; }
}
public string Error {
get { return ValidationHelper.GetError(SelfValidate()); }
}
public string this[string columnName] {
get {
var validationResults = SelfValidate();
if (validationResults == null) return string.Empty;
var columnResults = validationResults.Errors.FirstOrDefault(x => string.Compare(x.PropertyName, columnName, true) == 0);
return columnResults != null ? columnResults.ErrorMessage : string.Empty;
}
}
}
}
4-添加以下ValidationHelper:
#region Usings
using System;
using System.Text;
using FluentValidation;
using FluentValidation.Results;
#endregion Usings
namespace Core.Validation.Helpers {
public class ValidationHelper {
public static ValidationResult Validate(TK entity)
where T : IValidator, new()
where TK : class {
IValidator validator = new T();
return validator.Validate(entity);
}
public static string GetError(ValidationResult result) {
var validationErrors = new StringBuilder();
foreach (var validationFailure in result.Errors) {
validationErrors.Append(validationFailure.ErrorMessage);
validationErrors.Append(Environment.NewLine);
}
return validationErrors.ToString();
}
}
}
它允许您在应用程序代码中执行以下操作:
var operation = new Operation(){Name="A"}; var validationResults = operation.SelfValidate();
<TextBox Text ="{Binding CurrentOperation.Name,Mode = TwoWay,UpdateSourceTrigger = PropertyChanged,ValidatesOnDataErrors = True}">
注意:实现基于FluentValidation(.NET的一个小型验证库,使用流畅的接口和lambda表达式),请参阅http://fluentvalidation.codeplex.com/,但当然您可以使用另一个,希望我成功了描述从域对象解耦验证的机制.