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

使用 WCF 和自定义 DataGrid 在 Silverlight 中处理大型数据集

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (7投票s)

2010年4月5日

CPOL

7分钟阅读

viewsIcon

64606

downloadIcon

1763

使用基于 WCF 的服务和自定义 DataGrid 在 Silverlight 中显示、编辑和保存大型数据集

引言

本文简要介绍了上传和下载大型数据集及其进行编辑的高级技术。为此,仅使用了 Silverlight 3 随附的标准库。通信基于 WCF 服务,UI 只使用数据网格、按钮和文本框。本文的有趣之处在于,它向您展示了如何通过使用扩展类、反射和一些简单但功能强大的技巧来克服可能遇到的常见障碍。

背景

当我开始使用 Silverlight 时,我快速浏览了一些示例并很快构建了第一个示例应用程序。我在数据绑定和 XAML 方面遇到了一些小问题,但在理解了基本概念后,一切似乎都变得容易了。我找到了许多涵盖基本概念的优秀示例,它们为我打下了良好的基础,让事情看起来很简单。然后,我不得不实现第一个实际应用程序,事情开始变得棘手。我想实现的看似并不难——如果您在 WinForms 中实现它——但在 Silverlight 中,我很快就发现它不像在 WinForms/MFC 中那样工作。以下是应用程序应执行的操作:

  • 处理大量数据:上传和下载超过 50,000 行数据,数据量超过 10 MB
  • 快速显示数据(高性能)
  • 添加数据而不使用弹出对话框 - 只需在末尾始终保留一个空行
  • 排序数据
  • 过滤数据

这一切听起来很简单,但事实并非如此。第一个问题是,使用 WCF 服务上传超过 8 KB 的数据无法开箱即用。第二个问题是,向数据网格添加一个空行很容易,但结合排序功能,空行也会被排序,并且不总是在最后:如果您更改排序方向,它会变成第一行。最后,性能可能会成为一个问题。使用未修改的数据网格对 50,000 行数据进行排序需要几秒钟,这绝对是无法令人满意的,尤其是与 WinForms 相比。

本解决方案概述

基本上,本解决方案包含两部分:

  • 用于处理大型数据集的 WCF 服务
  • 数据网格的扩展,用于显示、编辑、排序和过滤数据

首先,我们将查看 WCF 服务,以便有一些数据可供操作。然后,我们将查看数据网格。此演示使用 Silverlight 3.0、.NET 3.5,并使用 Visual Studio 2008 编写。只需打开解决方案并按 F5 即可运行。我不会详细介绍如何部署它(客户端和服务器在不同机器上),也不会过多深入介绍如何使用 Visual Studio 2008 定义服务、XAML 文件等。已经有很多非常好的示例——包括 CodeProject 上的——所以我认为我无法做得更好或为这些示例添加任何内容。

WCF服务

WCF 服务在服务器端实现,是“MappingDataEditor.Web”项目的一部分。加载大量数据(几乎)可以开箱即用。在此示例中,将“test_data.csv”(位于子目录“TestData”中)的内容加载 50 次。该文件有 1,000 行,因此加载了 50,000 行,从服务器发送到客户端的数据量约为 11 MB。在“web.config”中,确保设置“dataContractSerializer”参数。

<dataContractSerializer maxItemsInObjectGraph="2147483647"/>

上传稍微复杂一些,因为您一次调用无法上传超过 8 KB 的数据。因此,数据会分块传输。客户端由 UploadState 对象管理。服务器端所需的函数如下所示:

[OperationContract]
Guid GetHandle(string pTableame);

[OperationContract]
void AddChunk(Guid pHandle, string pBlock);

[OperationContract]
bool Complete(Guid pHandle);

数据通过以下步骤传输:

  • 表中的数据被序列化。
  • 初始化 UploadState 对象,调用 StartUpload 后,通信完全由 UploadState 对象处理。
  • 使用 WCF 调用“GetHandle”将第一个块发送到服务器。
  • 然后使用 8KB 的块通过多次调用“PushOneBlock”来传输数据。
  • 每次块传输后,都会通知调用进程进度(您可以订阅“ProgressChanged”事件)。
  • 传输完最后一个块后,将调用“Complete”函数。此时,服务器端服务知道所有数据都已传输完毕,并可以执行任何需要对数据执行的操作(在本例中为保存数据)。

数据网格

数据网格使用 SortCollectionView 对象,该对象实现了 ICollectionViewIList 接口。

public class SortCollectionView<T> : ICollectionView, IList<T>
{
    private ObservableCollection<T> myContent = new ObservableCollection<T>();
    ...

这样,我们就可以自己实现排序和过滤。通过这种方式,我们可以确保用于输入新数据的空行始终位于末尾。我们只需删除最后一行,对数据进行排序,然后再次添加空行。

object selectedItem = myDGTable1.SelectedItem;
SortDescription sortDesc = myReturnTableView.SortDescriptions[0];
this.myReturnTableView.Content.RemoveAt(this.myReturnTableView.Content.Count - 1);
if (myChGeneric.IsChecked == true)
{
    myReturnTableView.Content.SortQuick(new GenericComparer<ReturnTableEntry>
	(sortDesc.PropertyName, sortDesc.Direction == ListSortDirection.Ascending, 
	StringComparison.CurrentCulture, myReturnTableView[0]));
}
else
{
    if (sortDesc.Direction == ListSortDirection.Ascending)
    {
        myReturnTableView.Content.SortQuick(new Field1ComparerUp());
    }
    else
    {
        myReturnTableView.Content.SortQuick(new Field1ComparerDown());
    }
}

myDGTable1.SelectedItem = null;
myReturnTableView.Content.Add(new ReturnTableEntry());
myReturnTableView.FireCollectionChanged();
myDGTable1.SelectedItem = selectedItem;

我们还可以通过传递一个 FilterCallback 函数并将其应用于数据来实现过滤。

public IEnumerator<T> GetEnumerator()
{
    if (myFilter == null)
    {
        return this.myContent.GetEnumerator();
    }
    else
    {
        Collection<T> col = new Collection();
        foreach (T item in this.myContent)
        {
            if (myFilter(item))
            {
                col.Add(item);
            }
        }

        return col.GetEnumerator();
    }
}

Using the Code

在简要了解了此示例应用程序的有趣部分之后,下面是与文件相关的有趣列表及其用途:

  • MainPage.xaml:XAML 中的 UI 定义,定义了 TabpageCanvasTableButtonsTextboxes 的布局。
  • MainPage.xaml.cs:包含大部分 UI 逻辑的代码隐藏文件。
  • SortCollectionView.cs:用于对 ObservableCollection 进行排序和过滤的对象。
  • ChangedSortDescriptionCollection.cs:需要访问“CollectionChanged”事件。
  • Extensions.cs:用于对 ObservableCollection 进行排序的泛型扩展方法(尝试使用 Bubblesort 并将其与 Quicksort 进行比较,看看有多慢)。
  • UploadState.cs:用于将大量数据作为 string 上传的辅助类。通过扩展此类的 ZIP 压缩器(可以在 此处 找到一个很棒的实现)可以加快上传速度。
  • IDataService.cs:WCF 服务的接口。
  • DataService.svc.cs:WCF 服务在服务器端的实现。
  • DataServiceHostFactory.cs:“ServiceHostFactory”,用于服务,以防止在使用多个服务时出现问题。
  • Default.aspx:托管 Silverlight 应用程序的网站。
  • UploadManager.cs:跟踪在将数据分块上传时接收到的块。
  • UploadSession.cs:单个上传会话的实现。
  • UploadSessions.cs:当前所有活动“UploadSession”的缓存。
  • ReturnTableEntry.cs:此示例中用于表示表中某一行的一个简单对象。

关注点 - 性能考虑

在评估性能时,我做出了一些非常有趣的观察。排序基于 Quicksort 算法。起初,我实现了一个泛型排序器,该排序器使用反射来确定要排序的列的数据类型。

this.myPropertyName = value;
this.myPropInfo = this.myTypeInstance.GetType().GetProperty(this.PropertyName);
if (this.myPropInfo.PropertyType.Equals(typeof(string)))
{
    this.CompareFunction = this.StringComparer;
}
else if (this.myPropInfo.PropertyType.Equals(typeof(int)))
{
    this.CompareFunction = this.IntComparer;
}
else if (...

根据类型,它会设置应使用的比较器。排序数据时,比较器会再次使用反射获取值。

private int StringComparer(T pTypeInstance1, T pTypeInstance2)
{
    string string1 = (string)myPropInfo.GetValue(pTypeInstance1, null);
    string string2 = (string)myPropInfo.GetValue(pTypeInstance2, null);
    ...

在 2.7 GHz 的 Core2Duo 处理器上,对表中 50,000 行数据进行排序需要 8 秒以上。但是,如果您不使用反射,而是传递一个直接访问属性的比较器,排序只需不到 1 秒,比前者快 10 倍以上。因此,反射对于实现通用解决方案非常有用,但对性能有很大影响。

第二件有趣的事情是,您第一次执行某些代码时,执行时间总是比后续调用长约 20%。这里可以看到 .NET 解释器对代码的影响。您可以通过查看应用程序的下半部分来尝试。在 textbox 中,您可以看到操作持续了多长时间。

另外,请注意 SortCollectionView。它在内部将数据存储在 ObservableCollection 中。这样,您可以直接设置来自 WCF 服务的数据,无需任何转换。因此,设置数据并显示它仅需 0.1 秒。如果您将数据复制到其他格式,则需要花费几秒钟。

备注

这只是一个示例应用程序,在使用它之前,您应该对其进行彻底测试。此外,此实现仅适用于 ObservableCollectionsProperties。它不适用于 DataViews。无论如何,我希望您喜欢它,并且它能为您节省大量时间。祝您玩得开心!

历史

  • 2010 年 4 月 5 日:测试应用程序“MappingDataEditor”的 Silverlight 3 版本 0.1
  • 2010 年 7 月 2 日:Silverlight 4.0 和 .NET 4.0 的新版本
    • 修复了复选框的一个错误
    • 添加了分页加载功能
    • 注意:性能未针对大型数据集进行优化。请查看 TPL 和并行循环。
    • 测试数据的访问路径在 Web.config 中硬编码。请编辑它,或直接将项目文件夹提取到“C:\”。
    • 按下“创建”按钮将生成一个新的、大型的测试数据文件,包含 100,000 行。之后,一次加载数据可能会失败。此时只能进行分页加载。请在“DataService.svc.cs”中调整要创建的行数。
© . All rights reserved.