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

RiaTasks:集中的 Silverlight 业务规则验证

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (28投票s)

2010年7月12日

Ms-PL

4分钟阅读

viewsIcon

83865

downloadIcon

803

在网站上使用 Fluent Validation 来验证 Silverlight 应用程序中的业务规则。

实时示例: http://silverlight.adefwebserver.com/riatasks2businessvalidation/riatasksweb/

管理网站中的 Silverlight 业务规则

为 Silverlight 提供验证的方法有很多。本文介绍了一种为 Silverlight 应用程序在启动该 Silverlight 应用程序的网站中提供业务规则验证的方法。您希望这样做的原因包括:

  • 多个共享同一数据库的 Silverlight 应用程序将共享相同的验证规则。
  • 使用同一数据库的普通 ASP.NET 应用程序将共享相同的验证规则。
  • 验证规则易于维护。

简单的 Silverlight 验证

本文接续博客文章基本的 Silverlight View Model 验证的讲述。在那篇博客中,我们介绍了客户端的简单验证。我们只关心验证用户输入的数据是否与底层数据类型匹配。在该示例中,我们验证了用户输入了有效日期,或者根本没有输入日期。

我们可以创建更多的验证规则,但是,如果存在多个View Models怎么办?我们需要在每个View Model中复制验证逻辑。即使我们集中了验证逻辑,也无法轻松地在多个 Silverlight 应用程序或使用同一数据库的任何 ASP.NET 网站之间共享。

Silverlight 业务规则验证应用程序

让我们来看看最终的应用程序。

如果您不输入姓名描述,应用程序将不会保存任何数据,并且会在屏幕底部一个可滚动框中显示错误消息。

如果您输入的日期是过去的日期,应用程序将不会保存任何数据,并且还会显示错误消息。

是的,如果您同时出现所有三种错误,它将在可滚动错误框中向您显示所有错误

网站业务规则验证

Silverlight 应用程序在 Web 服务器上处理所有验证。

上面的流程图解释了这个过程。

Fluent Validation

我们首先从Fluent Validation开始。它允许您使用 Linq 语法轻松创建业务规则。

您可以从这里下载Fluent Validation

我们将它的程序集添加到网站项目(而不是 Silverlight 项目)。

接下来,我们创建一个名为 _BusinessValidation_ 的文件夹和一个名为 _BusinessRules.cs_ 的文件,并将以下代码添加到其中:

using FluentValidation;
using System;

namespace RIATasks.Web
{
    // This validator will be used for all operations on the Tasks table
    public class TaskValidator : AbstractValidator<Task>
    {
        // These rules ensure that you always have a Name and Description for a Task
        public TaskValidator()
        {
            RuleFor(Task => Task.TaskName).NotEmpty()
                .WithMessage("Please specify Task Name");
            RuleFor(Task => Task.TaskDescription).NotEmpty()
                .WithMessage("Please specify Task Description");            
        }
    }

    // This validator will only be used for Insert operations on the Tasks table
    public class TaskInsertValidator : AbstractValidator<Task>
    {
        public TaskInsertValidator()
        {
            // When inserting the date must be null or in the future
            RuleFor(Task => Task.DueDate).Must(BeADateInTheFuture)
                .WithMessage("Please specify a date that has not already passed");
        }

        // If a non-null date is entered make sure it is in the future
        private bool BeADateInTheFuture(DateTime? dtCurrentDate)
        {
            DateTime dtDateInTheFuture = DateTime.Now.AddDays(1);
            return ((dtCurrentDate ?? dtDateInTheFuture) > DateTime.Now.AddDays(-1));
        }
    }
}

需要注意的一些事项。

  • TaskValidator() 将仅用于验证 Update 方法。
  • TaskValidator() TaskInsertValidator() 将用于验证 Insert 方法。
  • 如果数据库字段发生更改,此代码将无法编译(这是一个非常好的特性!)。

然后我们添加一个文件 _DataBaseParticalClasses.cs_,并将以下代码添加到其中:

using FluentValidation;
using System;
using FluentValidation.Results;
using System.Collections.Generic;
using System.ComponentModel;

namespace RIATasks.Web
{
    #region public partial class Task
    public partial class Task
    {
        public List<string> Errors = new List<string>();
    } 
    #endregion
}

这会向 Tasks 表添加一个 Errors 列。它提供了一个属性,可以将任何错误传递回 Silverlight 应用程序。您应该为每个数据库表都添加此项。

将以下代码添加到文件中:

public partial class RIATasksDBDataContext
{
    #region UpdateTask
    // This runs whenever the Task table is updated
    partial void UpdateTask(Task instance)
    {
        TaskValidator validator = new TaskValidator();
        ValidationResult results = validator.Validate(instance);
        
        if (!results.IsValid)
        {
            // There was an error 
            foreach (var failure in results.Errors)
            {
                instance.Errors.Add(failure.ErrorMessage);
            }
        }
        
        // Only proceed if there are no errors
        if (instance.Errors.Count == 0)
        {
            this.ExecuteDynamicUpdate(instance);
        }
    } 
    #endregion
    
    #region InsertTask
    // this runs whenever a record is Inserted into the Task table
    partial void InsertTask(Task instance)
    {
        TaskValidator validator = new TaskValidator();
        TaskInsertValidator Insertvalidator = new TaskInsertValidator();
        
        ValidationResult results = validator.Validate(instance);
        ValidationResult Insertresults = Insertvalidator.Validate(instance);
        
        if (!results.IsValid)
        {
            foreach (var failure in results.Errors)
            {
                instance.Errors.Add(failure.ErrorMessage);
            }
        }
        
        if (!Insertresults.IsValid)
        {
            foreach (var failure in Insertresults.Errors)
            {
                instance.Errors.Add(failure.ErrorMessage);
            }
        }
        
        // Only proceed if there are no errors
        if (instance.Errors.Count == 0)
        {
            // No errors - proceed with Update
            this.ExecuteDynamicInsert(instance);
        }
    } 
    #endregion
}

此代码实现了Linq to SQL类中的 Update Insert 部分方法。

此代码调用 TaskValidator() TaskInsertValidator() 方法中的验证代码。如果存在错误,它们将被添加到 Errors 字段,并且不会进行数据库更改。

如果没有错误,将为 Update 调用 this.ExecuteDynamicUpdate(instance),为 Insert 调用 this.ExecuteDynamicInsert(instance)

Web 服务

Web Service 方法需要修改以处理任何错误。

这是修改后的 Update 方法:

#region UpdateTask
[WebMethod]
public Task UpdateTask(Task objTask)
{
    RIATasksDBDataContext DB = new RIATasksDBDataContext();
    
    try
    {
        var result = (from Tasks in DB.Tasks
                      where Tasks.TaskID == objTask.TaskID
                      where Tasks.UserID == GetCurrentUserID()
                      select Tasks).FirstOrDefault();
                      
        if (result != null)
        {
            result.TaskDescription = objTask.TaskDescription;
            result.TaskName = objTask.TaskName;
            result.DueDate = objTask.DueDate;
            
            DB.SubmitChanges();
            
            // Set any errors 
            objTask.Errors = result.Errors;
        }
    }
    catch (Exception ex)
    {                
        // Log the error
        objTask.Errors.Add(ex.Message);
    }
    
    return objTask;
}
#endregion

Insert 方法:

#region InsertTask
[WebMethod]
public Task InsertTask(Task objTask)
{
    RIATasksDBDataContext DB = new RIATasksDBDataContext();
    
    try
    {
        Task InsertTask = new Task();
        
        InsertTask.TaskDescription = objTask.TaskDescription;
        InsertTask.TaskName = objTask.TaskName;
        InsertTask.UserID = GetCurrentUserID();
        InsertTask.DueDate = objTask.DueDate;
        
        DB.Tasks.InsertOnSubmit(InsertTask);
        DB.SubmitChanges();
        
        // Set the TaskID 
        objTask.TaskID = InsertTask.TaskID;
        
        // Set any errors 
        objTask.Errors = InsertTask.Errors;
    }
    catch (Exception ex)
    {
        // Log the error
        objTask.Errors.Add(ex.Message);
    }
    
    return objTask;
}
#endregion

Silverlight 应用程序

在 Silverlight 应用程序中唯一需要修改的 _cs_ 文件是 _TabControlModel.cs_ 文件。之前的 Message 属性已更改为 String 类型的 List,并添加了 MessageVisibility 属性。

Update 方法更改为:

#region UpdateTask
private void UpdateTask(Task objTask)
{
    // Call the Model to UpdateTask the Task
    TasksModel.UpdateTask(objTask, (Param, EventArgs) =>
    {
        if (EventArgs.Error == null)
        {
            // Show any errors
            Task objResultTask = EventArgs.Result;
            
            Message = objResultTask.Errors.ToList();
            // Set the visibility of the Message ListBox
            MessageVisibility = (Message.Count > 0) ? 
			Visibility.Visible : Visibility.Collapsed;
        }
    });
}
#endregion

Insert 方法更改为:

#region InsertTask
private void InsertTask(Task objTask)
{
    // Call the Model to Insert the Task
    TasksModel.InsertTask(objTask, (Param, EventArgs) =>
    {
        if (EventArgs.Error == null)
        {
            // Set the CurrentTaskID Property
            // So it can be selected when Tasks re-load
            CurrentTaskID = EventArgs.Result.TaskID;
            
            // Show any errors
            Message = EventArgs.Result.Errors.ToList();
            // Set the visibility of the Message ListBox
            MessageVisibility = (Message.Count > 0) ? 
			Visibility.Visible : Visibility.Collapsed;
            
            // If there are no errors - refresh Task List
            if (Message.Count == 0)
            {
                GetTasks();
            }
        }
    });
}
#endregion

显示错误

最后一步是在Microsoft Expression Blend 4+中打开项目,并将一个 ListBox 拖放到视图上……

……然后将 Message 集合从数据上下文窗口拖动……

……并将其拖放到 ListBox 上以创建绑定,使其显示错误消息。

接下来,在 ListBox 上,选择可见性旁边的高级选项

将其绑定到 MessageVisibility 属性。当没有错误时,这将隐藏 ListBox

简单验证

对于任何需要进行类型验证的 UI 元素,也就是说,您希望确保 Date 字段输入的是日期,或者数字字段输入的是整数,您可以使用以下方法:基本的 Silverlight View Model 验证

对于其他所有情况,您可以使用此处描述的方法。虽然这演示了仅验证Linq to SQL类,但相同的验证类也可以在 Web Service 方法中调用。这将使您能够创建跨多个表的复杂业务规则。

这主要使您能够将所有验证集中在一个地方,供多个Silverlight 应用程序和 ASP.NET 网站使用。

© . All rights reserved.