表示层简单的验证,我们可以使用基于对象属性内容的验证。在NHibernate Contributions中有一个可用的项目NHibernate.Validator。可以通过SVN下载NHibernate Contribution项目的源代码:地址是https://nhcontrib.svn.sourceforge.net,也可以通过NuGet获得。
一旦在解决方案中引用了NHibernate.Validator程序集,我们就需要配置验证引擎。为此,该项目定义了一个fluent API。首先,我们必须定义一个验证器配置的实例,如下面的代码所示:
var nhvConfig =new NHibernate.Validator.Cfg.Loquacious.FluentConfiguration();
注意这跟NHibernate配置不一样。
现在可以使用fluent API配置前面的配置对象和定义:
nhvConfig .SetDefaultValidatorMode(ValidatorMode.UseAttribute) .Register(typeof(Product).Assembly.ValidationDefinitions()) .IntegrateWithNHibernate .ApplyingDDLConstraints() .And .RegisteringListeners();
在上面的代码中,我们使用特性定义验证规则。还指示验证引擎解析我们定义的实体程序集。最后,请求验证引擎使用元信息应用到模型来丰富架构定义(调用ApplyDDLConstraints),还请求它定义两个使用NHibernate的截获器,无论何时插入或更新实体时都透明的验证属性(调用RegisteringListeners)。
有了这个验证配置,就可以创建验证引擎对象并使用前面的配置对象配置它了。如下面的代码所示:
var validatorEngine = new ValidatorEngine(); validatorEngine.Configure(nhvConfig);
最后为NHibernate配置对象使用由NHibernate.Validator项目定义的Initialize扩展方法。验证引擎对象作为Initialize方法的参数,如下面的代码所示:
nhibernateConfig.Initialize(validatorEngine);
在所有设置之后,我们准备创建或重新创建数据库架构,使用NHibernate的SchemaExport类,如下面的代码所示:
new SchemaExport(c).Execute(false, true, false);
完成了系统的配置和数据库架构的创建,就可以使用特性声明实体了。
每个特性都定义有一个Message参数,用于验证失败时显示的信息。最常用的一个特性还有NotNull,表示属性不能为空。一个产品必须有一个合法的名字,如下面的代码所示:
[NotNull(Message = "Product must have a valid name")] public string Name { get; set; }
通常,Product实体的Name必须至少有一个或两个且不超过50个字符。我们可以使用Length特性验证:
[NotNull(Message = "Product must have a valid name")] [Length(Min = 2, Max = 50, Message = "Name of product must be between 1 and 50 char")] public string Name { get; set; }
如上面的例子,我们可以在一个属性上定义多个特性。
如果不想在模型的属性上使用特性验证,我们可以使用NHibernatevalidator提供的fluent API配置实体的验证。
为我们想验证的每个实体定义一个继承自ValidationDef<T>的类,T表示我们想验证的实体。如果想验证Product实体,我们可以定义下面的类:
public class ProductValidator : ValidationDef<Product> { }
在上面类的构造函数中,我们使用NHibernate.Validator提供的fluent API以声明方式定义实体的验证逻辑。获得和前面的例子中相同的结果,我们可以使用下面的代码:
public ProductValidator() { Define(x => x.Name) .NotNullable() .WithMessage("The product name cannot be undefined") .And .LengthBetween(2, 50) .WithMessage("Product name must be between 2 and 50 char"); }
配置验证器的时候,我们注册了两个NHibernate监听器,当实体插入或更新时就会触发它们。如果实体处于不合法的状态,监听器就会抛出异常。
通常,我们不希望在验证失败时抛出异常。好点的解决方案就是在保存或更新实体之前就验证它们。如下面的代码所示:
var product = new Product {...}; var validator = new ValidatorEngine(); var invalidValues = validator.Validate(product); if (invalidValues.Length > 0) ShowInvalidValues(product, invalidValues); else session.Save(product);
上面的代码中,我们创建了一个product实体,然后使用验证引擎验证product。验证方法返回InvalidValue对象的数组。如果产品合法,数组的长度为0,否则数组的每个元素包含验证失败的信息。如果验证成功了,我们只保存产品,否则显示出验证失败的信息,如下面的代码所示:
private static void ShowInvalidValues(object entity, IEnumerable<InvalidValue> invalidValues) { Console.WriteLine(entity.GetType().Name); foreach (var invalidValue in invalidValues) Console.WriteLine(" Property {0}: {1}", invalidValue.PropertyName, invalidValue.Message); }
下面我们完成一个例子。
在这个例子中,我们想实现一个简单的模型,通过在属性上声明来验证。
1. 在MMSM中创建一个空数据库:BasicValidationSample。
2. 在Visual Studio中,创建一个控制台应用程序:BasicValidtionSample,并设置项目的Target framework为.NET Framework 4.0。
3. 添加对Castle.Core.dll, FluentNHibernate.dll,Iesi.Collections.dll, NHibernate.dll, NHibernate.ByteCode.Castle.dll程序集的引用,NHibernate.Validator我们通过NuGet添加。
4. 在Solution Explorer中,右击References文件夹,选择"Add Library Package Reference…"(在我的电脑上是Manage NuGet Packages),在弹出的对话框中,在左边选择Online,然后在搜索框中输入NHibernate.Validator,然后安装即可,如下图所示:
5. 在项目中添加一个Category类,如下所示:
public class Category { public virtual Guid Id { get; set; } [NotNullNotEmpty(Message = "The category name cannot be undefined.")] [Length(Min = 2, Max = 50, Message ="The category name must be between 2 and 50 characters long")] public virtual string Name { get; set; } }
Category的Name属性不能为空,且长度在2到50之间。
6. 添加Category的映射类CategoryMap,如下面的代码所示:
public class CategoryMap : ClassMap<Category> { public CategoryMap() { Id(x => x.Id).GeneratedBy.GuidComb(); Map(x => x.Name); } }
7. 在项目中添加一个Product类,代码如下所示:
public class Product { public virtual Guid Id { get; set; } public virtual string Name { get; set; } [NotNull] public virtual Category Category { get; set; } [NotNull] public virtual decimal UnitPrice { get; set; } [NotNull] [Min(1, Message = "Units on stock must be a positive number.")] public virtual int UnitsOnStock { get; set; } [NotNull] [Min(1, Message = "Reorder level must be a positive number.")] public virtual int ReorderLevel { get; set; } [NotNull] public virtual bool Discontinued { get; set; } }
Category,UnitPrice,UnitsOnStock,ReorderLevel,Discontinued都不能为空,UnitsOnStock和ReorderLevel必须是正数。
8. 添加Product的映射类ProductMap,代码如下所示:
public class ProductMap : ClassMap<Product> { public ProductMap() { Id(x => x.Id).GeneratedBy.GuidComb(); Map(x => x.Name); References(x => x.Category); Map(x => x.UnitPrice); Map(x => x.UnitsOnStock); Map(x => x.ReorderLevel); Map(x => x.Discontinued); } }
9. 在Program类中定义一个连接字符串,如下面的代码所示:
const string connString = "server=.;database=BasicValidationSample;" + "integrated security=true";
10. 在Program类中的Main方法中,使用Fluent NHibernate的API创建一个session工厂,代码如下所示:
static void Main(string[] args) { var factory = Fluently.Configure() .Database(MsSqlConfiguration .MsSql2008 .ConnectionString(connString) .ShowSql() ) .Mappings(m => m.FluentMappings .AddFromAssemblyOf<Product>() ) .ExposeConfiguration(ExportSchema) .BuildSessionFactory(); Console.Write("Hit enter to exit:"); Console.ReadLine(); }
11. 我们需要实现上面代码中由ExposeCofiguration方法调用的ExportSchema方法。在这个方法中,我们配置验证引擎,然后使用这个验证其配置初始化NHibernate配置,如下面的代码所示:
private static void ExportSchema(Configuration c) { var nhvConfig = new NHibernate.Validator.Cfg.Loquacious.FluentConfiguration(); nhvConfig.SetDefaultValidatorMode(ValidatorMode.UseAttribute) .Register(typeof(Product).Assembly.ValidationDefinitions()) .IntegrateWithNHibernate .ApplyingDDLConstraints().And.RegisteringListeners(); var validatorEngine = new ValidatorEngine(); validatorEngine.Configure(nhvConfig); ValidatorInitializer.Initialize(c, validatorEngine); new SchemaExport(c).Execute(true, true, false); }
在上面的例子中,我们通过NuGet快速添加了NHibernate.Validator.dll程序集,然后在我们的实体上声明了验证特性,最后配置NHibernate使用验证引擎在持久化到数据库之前验证实体。