【问题标题】:Server-side Validation for Silverlight ApplicationSilverlight 应用程序的服务器端验证
【发布时间】:2011-09-07 14:31:33
【问题描述】:

我正要实现 IDataErrorInfo,当我看到 INotifyDataErrorInfo 是用于异步验证时。进一步挖掘时,我注意到使用这些接口的示例都在 ViewModel 上。我需要对模型进行验证,并且需要与模型一起存储的错误以保持持久性。我有一个包含许多实体的大图。该图需要传回服务器以进行复杂的验证。我不确定我现在应该使用什么方法。

我是否只是将我的接口实现移动到模型中?

我看到的另一个例子有一个单独的验证服务。就我而言,我的验证规则很复杂,我正在考虑使用 Windows Workflow 及其规则引擎来提高验证规则的可维护性。

我需要单独的验证服务吗?

验证完成后,必须将图表传回客户端。然后需要显示任何错误/警告。

我是否应该在模型中实现 INotifyDataErrors 并在验证返回到客户端时引发事件以将错误发布到视图(通过 ViewModel)?

事实证明,我无法在类库中引用包含 INotifyDataErrors 的程序集。它会在共享这些类的程序集中产生冲突。

【问题讨论】:

  • Silverlight 的全部意义在于提供一个富客户端。这通常意味着客户端上的第一级验证(除了服务器上的任何验证)。 RIA 服务允许共享自定义验证器(在客户端和服务器上运行),但您的数据模型可能过于复杂而无法使用 RIA。
  • @HiTech Magic,RIA 肯定出局了。我已经有一个类库和一个 Silverlight 类库共享类。我使用启用 Silverlight 的 WCF 服务将数据从服务器发送到客户端。我确实有一些客户端验证,但这还不够。图表必须返回服务器进行全面验证。
  • 如果您的验证代码可用于客户端和服务器,您只需使用 RIA 服务项目的 .shared.cs 功能即可节省跨项目的链接文件。没有太多帮助,但比添加链接更简洁。
  • 有些验证可以由客户端和服务器共享,有些不能。我将只为什么添加链接,而不是将项目切换到 RIA。

标签: silverlight validation asynchronous server-side


【解决方案1】:

当您有大型项目时,RIA 可能不是一个好主意,例如具有不同层(服务、应用程序、域、基础架构)的应用程序。

前段时间,我不得不在具有复杂规则的 Silverlight 应用程序中实现验证。我使用的是通过实体框架生成的自我跟踪实体。我的需要之一是重新整理所有验证代码。

首先我尝试使用 EntLib 验证块并在客户端和服务器上使用相同的代码。这种方法不起作用,因为您会遇到一些问题,因为 SL 和 .NET4.0 使用不同版本的 DataAnnotations 程序集。

最后,我最终在服务器上编写了某种验证服务,该服务返回实体的错误(如果有)。像这样的:

interface IValidate
{
    IEnumerable<string> Validate(Entity entity); 
}

然后在 Client 上让 ViewModels 实现 INotifyDataErrorInfo(该接口支持异步验证),这样你就可以使用 Service 验证实体并将错误保存在 ViewModel 上。

class SomeViewModel : INotifyDataErrorInfo
{
    public Entity Entity { get; set; }

    public void Validate()
    {
        this.ClearErrors();
        // this method make the service calls
        var service = -- service instance --;
        var errors = -- get errors from service --;
        foreach (string error in errors)
            this.AddTopLevelError(error);
    }

    {...}
}

这样,所有验证逻辑都位于服务器上,并且它可以随时更改而不会影响客户端,因为所有实体在添加到数据库之前都会通过此服务传递(如果您正在使用)。

该服务还可以返回错误以及与错误相关的属性,这样您就可以与 Silverlight 进行更丰富的交互。所以服务可能是:

interface IValidate
{
    IEnumerable<PropertyError> Validate(Entity entity); 
}

class PropertyError
{
    public string PropertyName { get; }
    public IEnumerable<string> Errors { get; }
}

在这里您可以注意到,验证规则可能会在服务器上发生变化,而这个逻辑是如何实现的并不重要。所有这些都可以正常工作并满足您的要求,问题是 Silverlight 要求正在验证的对象包含所有有错误的属性。

在使用数据库时,这不是常见的场景,例如您可能会遇到的情况(这是一个简单的模型)

这个模型是使用 Entity Framework 4.1 完成的

因为如果您有一个用户实例并且想要访问电子邮件属性,您必须输入:user_instance.Person.Email。因此,电子邮件属性不在用户类型中,这是此解决方案的问题,因为您可能还想验证电子邮件。

这不是这样的吗,当您有一个带有实体(如上)的 ViewModel(实现 INotifyDataErrorInfo)并希望验证实体(在这种情况下为用户)时,您只需向属性 Entity.Person.Email.

但世界并不完美,所以我找到的解决方案是复制要在 ViewModel 上验证的每个属性,如下所示:

class SomeViewModel : INotifyDataErrorInfo
{
    public User Entity { get; set; }

    public string Name { get { return Entity.UserName; } set {...} }        
    public string Email { get { return Entity.Person.Email; } set {...} }

    {...}
}

通过这种方式,您可以将控件绑定到 ViewModels 属性而不是实体属性,但是使用更改通知会有点困难。

您可能还想查看:this 工具包。它解决了为您的实体定义一个 wapper 并使用 DynamicObject 模拟一个具有包装对象的所有属性的对象的问题。在处理大量数据时,这有点慢,但大大简化了工作。

希望这会有所帮助。

【讨论】:

  • 您的视图模型已通过服务器上的服务公开?您在实现 INotifyDataErrorInfo 服务器端时没有遇到问题吗?我做到了。
  • 不,INotifyDataErrorInfo 仅在客户端实现。该服务仅验证实体并返回错误。
  • 那你是什么意思:“然后在服务器上我让视图模型实现 INotifyDataErrorInfo”
  • @Josh 我已经解释了自己一点,检查解决方案。
  • 我明白了,但我还有最后一个问题。在您的场景中,您似乎一个接一个地验证了每个视图模型。在我的场景中,我有一个包含数十到数百个模型对象的视图模型,每个模型对象都可能需要单独验证。当我将实体传回服务器进行验证时,实际上是在传递一棵树(实体和所有子项)。我认为我不应该一个一个地传递每个实体,我需要一种方法来按实体包含错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-08
  • 1970-01-01
相关资源
最近更新 更多