用于开发 .NET 业务 Web 窗体的创新架构 (2) - 如何实现查询
本系列文章介绍了一种用于企业软件开发中开发业务 Web 窗体的创新架构,与传统的 ASP.NET 或 MVC 开发相比,它具有更好的性能、更高的生产力、更强的可配置性和更易于维护性。
引言
本系列文章介绍了一种用于企业软件开发中开发业务 Web 窗体的创新架构,与传统的 ASP.NET 或 MVC 开发相比,它具有更好的性能、更高的生产力、更强的可配置性和更易于维护性。这是本系列的第二篇文章。在继续之前,您应该在 <<RapidWebDevUI 概述>> 中查看第一篇文章以了解概念和概览。本文将介绍“查询”的工作原理以及如何在 Web 窗体中配置查询面板。
“查询”的工作原理
查询面板的开发由 XML 配置和对接口 IDynamicPage 的 Query 方法的实现组成,如下面的代码片段所示。
<QueryPanel HeaderText="Query Users">
    <TextBox FieldName="UserName" Label="UserName: " />
    <TextBox FieldName="DisplayName" Label="DisplayName: " />
    <CheckBoxGroup FieldName="Membership.IsApproved" 
        FieldValueType="System.Boolean" Label="Approved: " Occupation="1">
        <Item Text="Yes" Value="true" Checked="true" />
        <Item Text="No" Value="false" />
    </CheckBoxGroup>
</QueryPanel>
/// <summary>
/// Execute query for results binding to dynamic page grid.
/// </summary>
/// <param name="parameter">Query parameter.</param>
/// <returns>Returns query results.</returns>
QueryResults Query(QueryParameter parameter);
RapidWebDev UI 框架根据 QueryPanel XML 元素中的过滤器进行配置来渲染查询面板的 UI。默认支持的查询过滤器控件类型有 CheckBox、CheckBoxGroup、ComboBox、Date、DateTime、Integer、Decimal、TextBox 和 RaduiGroup。当所有这些控件都不能满足您的要求时,您可以使用 Custom XML 元素来配置您开发的自定义查询过滤器控件,该控件遵循自定义查询过滤器控件的规范。如上文的查询面板配置所示,渲染的 UI 如下面的屏幕截图所示。
 
 
当 RapidWebDev UI 框架渲染查询面板的 UI 时,有一个 JavaScript 管理器实例为查询面板注册到 Web 浏览器中,该管理器负责管理所有查询过滤器,包括它们的状态和值。当单击“查询”按钮时,查询面板管理器实例会收集每个查询过滤器的用户输入,并组装一个异步 HTTP 请求发送到动态页面的 RapidWebDev 数据服务以获取 JSON 数据响应,例如~/ObjectId/DynamicPageDataService.svc。我使用 Fiddler2 跟踪 HTTP 请求和响应,如下面的屏幕截图所示。返回 4 条记录的数据响应仅为 **2.5KB**。
 
 
最后,查询面板管理器将 JSON 数据渲染到网格中。
 
 
查询面板 XML 配置
上面提到的默认支持的查询过滤器控件类型具有通用属性,如 Label、FieldName、Operator、FieldValueType 和 Occupation。Label 是 UI 中查询过滤器的标签,例如“用户名:”。Label 支持变量标记,如 $VariableName$ 或“$Namespace.ClassName.StaticPropertyName, AssemblyName$”。VariableName 应通过接口 IDynamicPage 的 SetupContextTempVariables 方法进行设置。
FieldName 用于框架通过 IPredicateCompiler (命名空间:RapidWebDev.UI.DynamicPages)的实现自动组装查询表达式。它支持关联对象的属性。例如,我们配置一个动态页面来管理用户,页面的主要入口是 User。实体 User 引用 Membership。我们希望通过 Membership 的属性(如上面代码片段中的 Membership.IsApproved)来查询用户。Linq2SQL 谓词编译器(类:RapidWebDev.UI.DynamicPages.Linq2SQLPredicateCompiler)将查询过滤器编译为表达式,如
var query = from user in dataContext.Users
    where user.Membership.IsApproved
	select user;
 
 
Operator 是查询条件的运算符,支持 Equal、StartsWith、Like、In、NotIn、GreaterThan、GreaterEqualThan、LessThan、LessEqualThan 和 Between。默认情况下,框架根据查询过滤器控件类型选择运算符。
FieldValueType 是查询表达式参数值的 CLR 类型。如果指定了 FieldValueType,则在组装查询表达式时,查询过滤器控件的值将被隐式转换为目标类型。
Occupation 指示查询过滤器控件在渲染的查询面板中占用多少单元格。例如,查询面板布局实现 TableXColumnsQueryPanelLayout 在 HTML table 中渲染所有查询过滤器。Occupation 表示查询过滤器控件占用的列数。
除了这些通用属性外,最好查看 DynamicPage.xsd 以了解每个查询过滤器控件的扩展属性。
查询实现
RapidWebDev 动态页面的数据服务将异步查询请求转换为 QueryParameter,其中包含查询和排序信息。QueryParameter 可以通过 IPredicateCompiler (命名空间:RapidWebDev.UI.DynamicPages)的实现转换为查询和排序表达式,该实现已在 Spring.NET IoC 中配置。如下面查询产品的实现所示,我们只需要将编译后的查询和排序表达式与 Linq2SQL lambda 表达式进行转换和连接。而无需显式组装查询表达式。因此,我们可以轻松处理查询过滤器的需求变更,而无需重新编译。返回的 QueryResults(int recordCount, IEnumerable results) 会被框架自动序列化为 JSON 数据。
/// <summary>
/// Query products by parameters.
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public override QueryResults Query(QueryParameter parameter)
{
    using (ProductManagementDataContext ctx = 
        DataContextFactory.Create<ProductManagementDataContext>())
    {
        IQueryable<Product> q = from p in ctx.Products 
                                where p.ApplicationId == 
				authenticationContext.ApplicationId 
                                select p;
        LinqPredicate predicate = parameter.Expressions.Compile();
        if (predicate != null && !string.IsNullOrEmpty(predicate.Expression))
            q = q.Where(predicate.Expression, predicate.Parameters);
        if (parameter.SortExpression != null)
            q = q.OrderBy(parameter.SortExpression.Compile());
        int recordCount = q.Count();
        var results = q.Skip(parameter.PageIndex * parameter.PageSize)
            .Take(parameter.PageSize).ToList();
        return new QueryResults(recordCount, );
    }
}
动态数据
我们可能需要动态支持 XML 中无法配置的需求,例如,为具有不同权限的用户渲染不同的查询过滤器,或者查询面板中下拉列表框的项应从数据库中获取。 RapidWebDev 支持此类场景。动态页面的 XML 配置可以配置一个回调函数,在 UI 渲染之前更改配置,请参阅 DynamicPage.xsd 中的 Page/@ProcessCallbackType。我们可以在回调函数中在运行时更改配置。此外,我们可以为下拉列表框查询过滤器配置 DynamicDataSource。DynamicDataSource 被配置为从 JSON 驱动的外部 Web 服务拉取项,如下面的代码片段所示。因此,在该示例中,当用户选择或在可编辑的 combobox 中输入文本时,combobox 会从外部服务刷新匹配的项。
<ComboBox FieldName="ParentHierarchyDataId" Label="Parent: " Editable="true" 
    MinChars="2" FieldValueType="System.Guid" ForceSelection="true">
    <DynamicDataSource Url=
	"/Services/HierarchyService.svc/json/FindByKeyword?limit=999" 
	    Method="GET" TextField="Name" ValueField="Id" QueryParam="q">
        <Param Name="hierarchyType" Value="$HierarchyType$" />
    </DynamicDataSource>
</ComboBox> 
查询面板布局
IQueryPanelLayout 接口用于渲染查询面板的布局。 RapidWebDev 在 1.51 版本中实现了一个基于 html table 的布局,称为 RapidWebDev.UI.DynamicPages.TableXColumnsQueryPanelLayout。如果您有其他需求,可以实现一个新的策略并将其配置到 Spring.NET IoC 中,而不是当前 RapidWebDev.Web\Spring\ui.config 中的 TableXColumnsQueryPanelLayout。
什么是RapidWebDev
网站: http://www.rapidwebdev.org
RapidWebDev 是一个基础设施,可帮助工程师轻松高效地开发 Microsoft .NET 中的企业软件解决方案。它由一个可扩展且可维护的 Web 系统架构组成,包含一套通用的业务模型、API 和服务,作为开发几乎所有业务解决方案所需的基本功能。因此,当工程师使用 RapidWebDev 进行开发时,他们可以获得许多可重用且现成的东西,然后可以更专注于业务逻辑的实现。实际上,与传统的 ASP.NET 开发相比,我们可以节省超过 50% 的时间来开发高质量、高性能的业务解决方案。



