业务逻辑层的设计(二)——让领域模型支持验证
在之前的随笔《业务逻辑层的设计(一)》已经提到了领域对象模型如何支持验证了。
本篇随笔将对领域模型继续强化。
DomainObject为所有领域模型的基类,只要让我们的领域模型继承这个基类,也就实现了通用的验证接口ISupportsValidation。
ConstraintValidator中封装了使用验证框架的操作,配合ValidationErrorValidationError使外界调用不需要了解验证框架的API。
public interface ISupportsValidation { bool IsValid { get; } IList<ValidationError> Validate(); }
ISupportsValidation接口使得所有的领域模型都具备验证的功能,而DomainObject则实现了具体的验证,使得我们所有的领域模型都有通用的验证模块。
/// <summary> /// 所有领域模型的基类 /// </summary> public class DomainObject:ISupportsValidation { public virtual bool IsValid { get { try { return ConstraintValidator.IsValid(this); } catch { return false; } } } IList<ValidationError> ISupportsValidation.Validate() { IList<ValidationError> errors; errors = (IList<ValidationError>)ConstraintValidator.Validate(this); return errors; } }
其中的ConstraintValidator类中引用了微软企业库5.0的验证模块,这个设计是为了保持其他层对验证框架的透明化,使用时也就不必引用验证框架了。
using Microsoft.Practices.EnterpriseLibrary.Validation; public class ConstraintValidator { public static bool IsValid(DomainObject domainObj) { ValidationResults results = Validation.Validate(domainObj); return results.IsValid; } public static IList<ValidationError> Validate(DomainObject domainObj) { IList<ValidationError> errors = new List<ValidationError>(); ValidationResults results = Validation.Validate(domainObj); if (!results.IsValid) { foreach (ValidationResult result in results) { ValidationError error = new ValidationError(); error.Key = result.Key; error.Message = result.Message; errors.Add(error); } } return errors; } }
使用微软企业库来简化代码的复杂度,将会看到User继承了DomainObject,
但是在领域模型中还是需要引入using Microsoft.Practices.EnterpriseLibrary.Validation命名空间和属性,
但比起完全自己动手写规则,的确要简化很多了。
public class User : DomainObject { private Department _department = new Department(); private IList<Role> _roles = new List<Role>(); /// <summary> /// ID /// </summary> [NotNullValidator(MessageTemplate="标识不能为空")] public virtual int ID { get; set; } /// <summary> /// LoginName /// </summary> [StringLengthValidator(50, MessageTemplate = "登录名称必须在50个字符以内")] public virtual string LoginName { get; set; } /// <summary> /// Password /// </summary> [StringLengthValidator(20, MessageTemplate = "密码长度必须在20个字符以内")] public virtual string Password { get; set; } /// <summary> /// DepartmentID /// </summary> public virtual Department Department { get { return _department; } set { _department = value; } } /// <summary> /// Name /// </summary> public virtual string Name { get; set; } /// <summary> /// CreateTime /// </summary> public virtual DateTime CreateTime { get; set; } /// <summary> /// Roles /// </summary> public virtual IList<Role> Roles { get { return _roles; } set { _roles = value; } } }
可以发现领域模型比上一次的要简洁很多,细心的同学应该发现类中使用了许多virtual,
那是为了支持数据访问层的延迟加载功能,关于延迟加载原理可以看下 另一篇 。
让我们来体验一下吧
[Test] public void TranRollbackTest() { IDataContext context = DataContextFactory.GetDataContext(); User user1 = new User() { Name = "adama1", Password = "123123" }; User user2 = new User() { Name = "adama2", Password = "123123" }; if (!user1.IsValid) { ISupportsValidation user1Vali = user1; IList<ValidationError> errors= user1Vali.Validate(); foreach (ValidationError error in errors) { Console.WriteLine("{0}:{1}",error.Key,error.Message); } } context.BeginTransaction(); context.Add(user1); context.Add(user2); context.Rollback(); }
由于我们在LoginName属性加了验证条件,所以验证并不会通过,
输出结果:LoginName:登录名称必须在50个字符以内
上面的context是我探索ORM原理时自制的上下文,也是一个用到了前一篇提到的工作单元uow,暂时不必理会。