实时数据流技术实现

在现代的Web应用中,实时数据流是一个重要的功能,它允许客户端及时接收并展示服务器端的数据。本文将探讨如何使用ASP.NET Web API在后台线程中将大型结果集流式传输到WPF客户端,并在内容接收后立即在屏幕上显示。此外,本文还将展示如何使用Dapper,一个简单的ORM,逐条返回记录的结果。

本文将通过示例代码来演示这一概念,该代码允许用户通过输入姓名的首字母来搜索人员,并显示匹配搜索条件的人员列表。

使用代码

解决方案包含两个项目:服务项目和服务端项目。解决方案应包含运行示例代码所需的一切,只需确保已安装.NET Framework 4.5,因为代码使用了新的async和await关键字。为了调试目的,同时启动两个项目。

服务项目是一个Web API MVC4项目。有两个控制器,HomeController和NameController。HomeController是在创建项目时默认创建的,在这个案例中不会使用它,所以会忽略它。Name控制器包含将展示Web API流式传输能力的服务。

在Name控制器中有两个动作:SimulateLargeResultSet动作和UsingDapper动作。为了正确地将请求路由到相应的动作,需要添加以下路由到WebApiConfig.cs中。

public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "Api", routeTemplate: "api/{controller}/{action}", defaults: new { first = string.Empty } ); }

SimulateLargeResultSet动作展示了当从SQL Server返回大型结果集时会发生什么。检索大型结果集需要时间。为了模拟延迟,在for循环的末尾添加了Thread.Sleep(250)。它将创建一个带有Id和名称的Person对象,以返回给客户端。

UsingDapper动作默认不使用,因为它需要设置SQL Server数据库。逻辑与SimulateLargeResultSet动作非常相似,但它包含连接到SQL数据库并从Name表中读取的代码。需要创建数据库结构并更改连接字符串才能使其工作。使用Dapper v1.12.0.0执行SQL查询,并逐条返回person对象。这样做的好处是不必等待整个结果返回后再将输出推送回客户端。将buffered参数设置为false,并将commandtimeout设置为无限期。

WPF客户端项目使用Prism 4.1和MVVM方法。运行示例代码时,将看到一个FirstName文本框,用户可以在其中输入要搜索的人的姓名,以及一个datagrid,它将显示服务项目中的所有结果。

MainWindow.xaml有一个ViewModel,名为MainWindowViewModel,其构造函数中有以下代码行。

BindingOperations.EnableCollectionSynchronization(Persons, _personsLock);

这行代码允许从后台线程修改Observable集合,而不会抛出WPF错误。WPF要求所有UI更改代码必须在调度线程上运行,即UI线程。在.NET Framework的较新版本中,它会自动将所有INotifyPropertyChanged.PropertyChanged事件委托到UI线程,但它不会为INotifyCollectionChanged.CollectionChanged做这件事。必须使用EnableCollectionSynchronization(...)或手动将添加/删除操作分派回UI线程。发现使用BindingOperations比使用调度器要快得多。

DataGrid绑定到MainWindowViewModel类中的Persons属性,该属性是ObservableCollection类型,将实时向其中添加person对象。

public ObservableCollection Persons { get { if (_persons == null) { _persons = new ObservableCollection(); } return _persons; } set { _persons = value; RaisePropertyChanged(() => Persons); } }

在ViewModel中,有两个命令,搜索命令和取消命令。搜索命令简单地启动一个后台线程,调用Web API服务流,并返回一个Person列表。然后,它将person对象添加到Persons ObservableCollection中,这将实时反映在UI中。

using (Stream stream = await response.Content.ReadAsStreamAsync()) { byte[] readBuffer = new byte[512]; int bytesRead = 0; while ((bytesRead = stream.Read(readBuffer, 0, readBuffer.Length)) != 0) { ct.ThrowIfCancellationRequested(); string personString = Encoding.UTF8.GetString(readBuffer, 0, bytesRead); var person = JsonConvert.DeserializeObject(personString); persons.Add(person); } }

在While循环的开始,它检查是否已请求取消令牌:

ct.ThrowIfCancellationRequested()
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485