我正在使用MVVM模式构建一个C#
WPF应用程序.我有使用NHibernate来保存我的域模型的存储库类.
@H_404_2@我的模型由一个更大的树结构组成(包含含有阶段的操作的配方).操作和阶段都包含键值映射的动态列表,作为IDictionary< string,string>.操作的相应NHibernate映射是
将该功能添加到我使用NHibernate存储的模型类中,或者模型类是否保持“哑”,这是一种良好的做法吗?
>为包含我的Operation对象的验证逻辑创建一个装饰器类. @H_404_2@这样我每次需要验证时都会使用包装器,而只需要显示它就可以使用裸模型类.我的问题是我的viewmodel类已经是包装器,所以我会在这里得到另一层包装.此外,由于Operation类是较大树结构(配方/操作/阶段)的一部分,我需要为集合创建包装器,并将集合更改映射回底层集合,这可能是一项复杂的任务.
>创建一个服务类,每当我想验证它时,我都会调用该服务. @H_404_2@我在这里看到的问题是该服务是无状态的,因此每次用户更改单个参数时都必须重新验证整个参数列表.这似乎不是最好的方法,特别是当我想在参数的验证状态发生变化时为UI触发某种更改事件时. @H_404_2@我的问题的常见方法是什么?是否有一种我还没有找到的模式,这完全是我需要的?我的意思是,有很多实现依赖于外部模式定义进行验证(阅读:XML / XSD和类似的文档结构),只需要有一些天才已经找到了解决我问题的完美解决方案;-)帮帮我吧!
<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>@H_404_2@现在,这部分很简单,效果很好. Operation类目前是一个几乎没有逻辑的POCO,一个简单的数据容器. @H_404_2@我的问题是:我必须根据我的应用程序从.xml文件中读取的外部模式验证参数.该模式包含对单个参数(范围,有效值等)的限制以及若干参数之间的依赖性(即,有效值根据另一个参数的值而变化). @H_404_2@集成验证逻辑的最佳方法是什么?我在最近几天看了很多,直到现在,我偶然发现了以下几种选择: @H_404_2@>将验证逻辑添加到模型类本身. @H_404_2@为此,我不知道如何将验证模式正确地注入到NHibernate创建的对象中.我不需要一直使用验证功能,只有当用户正在编辑参数或我正在导入操作时(例如从备份中).那么也许我可以在模型类中实现实际的验证逻辑,并在我真正需要验证时使用属性注入验证规则?
将该功能添加到我使用NHibernate存储的模型类中,或者模型类是否保持“哑”,这是一种良好的做法吗?
>为包含我的Operation对象的验证逻辑创建一个装饰器类. @H_404_2@这样我每次需要验证时都会使用包装器,而只需要显示它就可以使用裸模型类.我的问题是我的viewmodel类已经是包装器,所以我会在这里得到另一层包装.此外,由于Operation类是较大树结构(配方/操作/阶段)的一部分,我需要为集合创建包装器,并将集合更改映射回底层集合,这可能是一项复杂的任务.
>创建一个服务类,每当我想验证它时,我都会调用该服务. @H_404_2@我在这里看到的问题是该服务是无状态的,因此每次用户更改单个参数时都必须重新验证整个参数列表.这似乎不是最好的方法,特别是当我想在参数的验证状态发生变化时为UI触发某种更改事件时. @H_404_2@我的问题的常见方法是什么?是否有一种我还没有找到的模式,这完全是我需要的?我的意思是,有很多实现依赖于外部模式定义进行验证(阅读:XML / XSD和类似的文档结构),只需要有一些天才已经找到了解决我问题的完美解决方案;-)帮帮我吧!
解决方法
>将验证逻辑添加到模型类本身.
– 不是最好的方法,因为你会用逻辑炸毁POCO,你最终会使用ActiveRecord模式,它不是那么干净. @H_404_2@>为包装我的Operation对象的验证逻辑创建一个装饰器类.-认为这是一个更好的方法,区别在于你必须包装已经存在的包装器,并且你最终也会吹出抽象级别,所以也不推荐. @H_404_2@>创建一个服务类,我都会调用该服务.
– 也许你不能完成这些事情(如果我正确地理解你是在谈论web服务或其他类型的远程服务),如果你对这些验证规则有限制,这个解决方案更合适集中为多个客户端而不是紧密的具体应用程序. @H_404_2@我赞成以下解决方案: @H_404_2@将验证器项目添加到包含以下内容的解决方案: @H_404_2@>根据应用程序从.xml文件中读取的外部模式验证参数的逻辑.
>您在项目中使用的每个POCO对象的验证规则,并且它需要验证(或者,您也可以在更高级别应用这些规则,如果您已经有这样的实现,则意味着不是POCO而是POCO上的一些Wrapper,但是尝试将规则直接应用于POCO – 更清洁,更正确的方法 @H_404_2@所以: @H_404_2@1 – 您的POCO将包含属性和简单验证SelfValidate():
– 不是最好的方法,因为你会用逻辑炸毁POCO,你最终会使用ActiveRecord模式,它不是那么干净. @H_404_2@>为包装我的Operation对象的验证逻辑创建一个装饰器类.-认为这是一个更好的方法,区别在于你必须包装已经存在的包装器,并且你最终也会吹出抽象级别,所以也不推荐. @H_404_2@>创建一个服务类,我都会调用该服务.
– 也许你不能完成这些事情(如果我正确地理解你是在谈论web服务或其他类型的远程服务),如果你对这些验证规则有限制,这个解决方案更合适集中为多个客户端而不是紧密的具体应用程序. @H_404_2@我赞成以下解决方案: @H_404_2@将验证器项目添加到包含以下内容的解决方案: @H_404_2@>根据应用程序从.xml文件中读取的外部模式验证参数的逻辑.
>您在项目中使用的每个POCO对象的验证规则,并且它需要验证(或者,您也可以在更高级别应用这些规则,如果您已经有这样的实现,则意味着不是POCO而是POCO上的一些Wrapper,但是尝试将规则直接应用于POCO – 更清洁,更正确的方法 @H_404_2@所以: @H_404_2@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 } }@H_404_2@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; }); } } }@H_404_2@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; } } } }@H_404_2@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(); } } }@H_404_2@它允许您在应用程序代码中执行以下操作: @H_404_2@>在服务或视图模型级别,您可以执行此操作以获取验证错误:
var operation = new Operation(){Name="A"}; var validationResults = operation.SelfValidate();@H_404_2@>在View级别,您可以编写类似这样的内容(在这种情况下,如果出现任何验证错误,则直接从OperationValidator类获取):
@H_404_2@ <TextBox Text=”{Binding CurrentOperation.Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}”>@H_404_2@注意:实现基于FluentValidation(一个使用流畅接口和lambda表达式的.NET小型验证库),请参阅http://fluentvalidation.codeplex.com/,但当然您可以使用另一个,希望我成功描述了从域对象解耦验证的机制.