65.9K
CodeProject 正在变化。 阅读更多。
Home

设置 Silverlight、RIA Services、NHibernate 和验证器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.44/5 (9投票s)

2010年10月5日

CPOL

9分钟阅读

viewsIcon

32948

downloadIcon

2353

如何设置一个基本的应用程序,使用 Silverlight、RIA 和 NHibernate 进行端到端验证,一切都很简洁:)

将所有这些解压到一个专门的空文件夹中

引言

RIA Services 是一个相对较新的技术,因此很难找到有关如何为其设置一个合适的商业应用程序开发环境的相关信息和代码示例(这可能是整个框架的主要目的)。尤其是在您之前的项目中,如果您已经深入了解了 NHibernate 及其强大的功能和工具,并且只是想将所有这些都整合进来。RIA 的一个关键点是其数据源无关的方法,这意味着您可以将几乎任何东西连接为数据源。这基本上是真实的。而且您还可以获得一个不错的端到端验证。这也是“基本上”真实的。但有没有一些示例代码,最好是使用 Fluent NHibernate、ORM 验证以及一些自动映射来增加风味?好吧,到目前为止,您可能运气不佳:)。

验证问题

我想要解决的一个关键问题是验证流程。正如大家所知,通过使用 System.ComponentModel.DataAnnotations 命名空间,可以很好地支持端到端验证。它既可以作为 .NET 程序集,也可以作为 Silverlight 程序集。
通过使用这个程序集,我们可以在客户端和服务器端获得很好的验证。如果我们使用 Entity Framework 或 Linq2Sql,我们还将获得数据层验证。我们唯一需要做的就是用正确的属性来修饰我们的业务对象。例如,我们将使用 Plate 类。

public class Plate
{         
    [Key] 
    public virtual int Id { get; set; } 
    //[Required(ErrorMessage = "Please provide a name")]
    public virtual string Name { get; set; } 
    [CustomValidation(typeof(PlateDescriptionRule),"Validate")]
    public virtual string Description { get; set; }        
    [Required] 
    [Range(0, 10000)] 
    public virtual int? CalorieCount { get; set; } 
    public virtual int? RestaurantId { get; set; } 
             
    [Include] 
    [Association("RestaurantPlate", "RestaurantId", "Id", IsForeignKey=true)]
    public virtual Restaurant Restaurant { get; set; }
} 

示例的重点是 CalorieCount Description 属性。在前面的代码片段中,它们已经使用了属性进行描述。如前所述,这对于基于 Microsoft 工具的解决方案来说效果很好。但是如果我们想在数据层进行一些验证,并改用 NHibernate 呢?我们首先需要做的是选择正确的验证框架。与 NHibernate 配合最好的框架是 NHibernate Validator。它与 ORM 集成得非常好,我几乎可以用它做各种事情。NHibernate Validator 有自己的一套属性,但将另一套类似的属性放在同一个类上最终会显得很荒谬。

public class Plate  
{
     [NotNull] //NHV attribute
    [Required] //DA attribute for the same thing
    [Range(0, 10000)]
    public virtual int? CalorieCount { get; set; }
}  

这也是我不喜欢使用属性的最大原因之一。在当今的商业应用程序世界中,需要解决的层和关注点太多了,仅仅将属性应用到模型上会很快变得非常陈旧,并且不易维护。所以,如果您有 3 个不同的系统在同一个业务模型上使用属性,您将很难弄清楚哪个属性属于哪里。此外,使用模型提供的灵活性也不够,通常您不得不依赖“魔术字符串”。什么是魔术字符串?一个很好的例子就是对 description 属性实现的自定义验证器。

    [CustomValidation(typeof(PlateDescriptionRule),"Validate")]
    public virtual string Description { get; set; }  

因此,这个属性告诉我们应该使用 PlateDescriptionRule 类中的 static Validate”方法,该方法还必须有一个参数,其类型与 Description 属性相同。我知道,这需要一些时间才能让您的大脑理解。也不太方便重构。
幸运的是,RIA 和 NHV 都对替代方法持开放态度。

合并验证

有些人可能会问,为什么还要费力实现 NHV 呢?服务器端和客户端都有验证,所以基本上没有数据能在被验证之前进入服务器。嗯,正如您所知,在与服务器通信时,并非所有数据都直接进入数据库。在保存数据之前,通常会进行一些预处理,在此过程中,一些有效数据可能会变得无效。这通常是一个设计缺陷,但它仍然是一个需要解决的缺陷。此外,您还可以使用某种直接访问数据库的方法,使用相同的模型程序集和 NHibernate 类,完全避免 Silverlight 注释,仍然可以获得验证的好处。无论如何,我认为只需稍微多花点精力就能做出一个好的设计。另外,在类型安全和重构方面,整个事情都有一些很棒的收益,这得益于 NHV 更强大的模型,它会覆盖 RIA 的模型。

示例代码本身几乎就是每个人从所有这些中需要的东西,但我想评论一些设计决策。这是使用 NHibernate 和 RIA 的基本示例,您可以在 RIA 网站上找到,已更新到 NHibernate 和其他工具的最新版本(所有都来自 trunk 并已编译),NHibernate 也是如此。

该解决方案的基石是 RIANHV. 的流畅接口。

正如您从 RIA 示例中看到的,Metadata 类用作关联属性的容器。它最棒的地方在于该类不继承自任何其他类,即它是独立的。Nikhil 做得很棒,但我对原始概念进行了一些重构,以适应我的编码方式(引入了一个接口 Imetadata,进行了一些重命名、重排职责等...)。NHibernate validator 的 Fluent 接口基于 IValidationDef 类,您需要继承它来定义属性和属性之间的关联。它们都是通用的,所以合乎逻辑的做法是让 Metadata 类继承自 IvalidationDef。这样,RIA 和 NHV 都可以使用同一个类来进行验证定义,而 MetadataClass 则包含 RIA 和 NHV 的属性。唯一要做的就是以某种方式声明相应的属性,为此我们使用 IcombinedRule 接口。

public interface ICombinedRule
{
    Attribute NhAttribute { get; } //must implement IRuleArgs
    ValidationAttribute DNetAttribute { get; }
    string ErrorMessage { get; }
}

所以,如果我们看前面提到的“非空”属性验证的例子,我们会这样定义它:

    public class RequiredRule:ICombinedRule
    {
        public Attribute NhAttribute
        {
           get { return new NotNullAttribute(); }
        }

        public ValidationAttribute DNetAttribute
        {           
           get { return new RequiredAttribute(); }
        }
    }  

并像这样使用它:

public class PlateMetaData:Metadata<Plate>
    {
       public PlateMetaData()
       {
           AddMetadata(new RequiredRule(),errorMessage);
//or alternatively i adjusted the existing fluent interface which we be   
//like this: 
    this.Validation(x => x.CalorieCount).Required();         
       } 
    } 

这里也涉及到一些 Fluent 接口定义,我展示了两种添加“非空”验证的方法。整个练习的重点是有一个验证定义的地方,并在所有层上进行多次验证,并带有相应的消息。

自定义验证

对我来说,引入合并验证模型的一个更大好处是自定义验证。RIA 的做法(如前所述)很容易出错。虽然错别字方法名或定义错误会导致编译时错误,但由于重构的缘故,它仍然不令人满意。NHV 的方法对我来说更可靠。它要求您定义自定义属性,然后定义要用于该特定属性的验证器,该验证器必须实现某个接口。

[ValidatorClass(typeof(PlateDescriptionRule))] 
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class PlateDescriptionAttribute : Attribute, IruleArgs
{
    public string Message
    {
        get { return PlateDescriptionRule.Message; }
        set
        {
            if (value == null) throw new ArgumentNullException("value");
        }
    }
} 

PlateDescriptionRule 类将如下所示:

public class PlateDescriptionRule:Ivalidator
{ 
    public static string Message = "Valid descriptions must have 5 or more words.";
    public static ValidationResult Validate(string description)
    {
        if (description != null && description.Split().Length < 5)
        {
            var vr = 
            new ValidationResult(Message, new string[] { "Description" });
            return vr;
        }
        return ValidationResult.Success;
    }  
    public bool IsValid(object value, 
	IConstraintValidatorContext constraintValidatorContext)
    {
        return Validate((string)value) == ValidationResult.Success;
    } 
}

为了应用整个内容,我们只需执行以下操作:

public PlateMetaData() 
{ 
    this.Validation(x => x.Description).Custom<PlateDescriptionAttribute>();
}   

所以,我们在这里实际做的是利用 NHV 的自定义验证机制为 RIA 服务提供验证!嗯,不完全是 :) 但通过一些巧妙的约定和组合使用,解决方案变得更易于管理。RIA 处理自定义验证的方式是提供类和我们想要用于验证的 static 方法的名称。这可以用来有一个大类和许多 static 验证方法,所有这些都必须在 string 中定义。这样,我们强制创建独立的验证类来处理我们想要定义的每一个自定义验证。我们必须遵守的唯一约定是,在每个此类中都有一个 static Validate”方法供 RIA 使用。
我是一个通过代码强制执行良好实践的大力倡导者,即不提供最佳方法的替代方法。这样,开发人员将被迫以类型安全的方式定义自定义验证,并遵守某些给定约定。我认为,在这方面,微软有很多可以向社区解决方案学习的地方。

锦上添花

为了让事情更完美,我添加了对整个内容的 Fluent 配置,您可以在 RestaurantRepository 类中找到,使用约定进行自动映射,而不是那些丑陋的 XML,
NHibernate 3.0 Alpha2、编译的 Fluent NHIbernate、编译的 NHibernate Validator 以及其他许多东西。我不怎么提倡自动映射(我们使用代码生成来代替映射),但它是小型项目如何设置自动映射的一个很好的例子。此外,如果您出于某种原因选择在某些模型上使用属性,而在其他模型上使用 Fluent,则通过使用属性进行的验证仍然有效。

总而言之,这个项目有很多值得一看的地方,即使您对这类验证不感兴趣,只想坚持使用 Microsoft 的方法。

注意事项

好吧,这种方法存在一些问题,最明显的一个是“Nhibernate-Silverlight”项目。这是怎么回事?嗯,对于客户端自定义验证,您还必须共享验证器代码,这意味着您必须定义验证器实现的接口。基本上,该项目除了让整个东西编译没有错误之外,什么都没做。这些问题可以通过引入额外的抽象层来避免,但我决定就这样,以便您了解潜在的问题。实际上,我从不直接与 NH 或 NHV 对话,在严肃的项目中,我总是用自定义类包装它们,而且还有一些依赖注入,以避免在这个项目中遇到的一些问题。

结论

RIA services 和 Silverlight 为商业软件开发人员带来了很多好处,它们抽象了许多您在没有它们的情况下必须以非常痛苦的方式克服的管道和问题。但更棒的是,尽管它做了很多工作,但它并不限制您以自己的方式做很多事情,验证就是一个很好的例子。Nhibernate 3.0 将随附开箱即用的 LINQ 提供程序,这对于与 Ria services 的无缝集成至关重要,并且其功能日益强大。最棒的是,您可以几乎直接从 Nhibernate trunk 运行您的项目,而无需过多考虑。这只是您可以根据需要全面调整 Ria 和 Nhibernate 的冰山一角,我本人也正在探索其潜力。

这是我的第一篇文章,因此任何评论、批评或建议都非常欢迎。
希望您喜欢:)

历史

  • v1.0 只是为了发布一下 :)
© . All rights reserved.