Silverlight 教程:使用 DataGrid、LINQ 和 WCF Web 服务创建以数据为中心的 Web 应用






4.17/5 (17投票s)
一个分步指南,介绍如何创建能够向数据库提交和检索数据的 Silverlight Web 应用。
您可以在我的网站上看到此完成的以数据为中心的 Silverlight 2.0 Web 应用程序的有效示例。
引言
本文是一个分步教程,介绍如何创建以数据为中心的 Silverlight 2.0 Web 应用程序。本教程将让您对以下所有内容获得实际动手经验:
- Silverlight 2.0 DataGrid
- LINQ
- Web 服务
如果您仔细阅读本教程,我认为您会对这三种技术是多么简单、强大和优雅感到惊讶。我真的开始觉得微软在这些新技术方面做得非常出色。
我还会为您提供有关如何将此应用程序部署到远程 Web 服务器的说明,在我看来,这可能是最困难的部分。我提供的说明特别适用于我的主机提供商discountasp.net;但是,无论您的托管提供商是谁,您可能都需要做类似的事情。
第一部分:构建应用程序并在本地进行测试
- 本教程需要您可以访问 Web 可访问的数据库。如引言中所述,我使用的是来自discountasp.net的 Web 托管帐户,带有 SQL Server 2008 附加组件,但也有许多托管提供商可供选择。要与远程数据库建立连接,请从菜单中选择“视图”,然后选择“服务器资源管理器”。单击“连接到数据库”选项,然后填写您的托管提供商提供的 SQL Server 名称和数据库登录名。单击“测试连接”以确认设置正确。
- 创建一个名为 SilverlightClient 的新 Silverlight 应用程序。将解决方案命名为 WcfSqlDemo。
- VS 2008 会询问您是否要将 Web 添加到解决方案。请选择“Web 应用程序项目”,而不是默认的“Web 站点”。将 Web 命名为 WcfSqlDemoWeb。
- 右键单击 WcfSqlDemoWeb,然后选择“添加新项”。选择“数据”->“LINQ to SQL 类”,并保留名称DataClasses1.dbml。
- 如果您在项目开始时设置了服务器,那么您已经在服务器资源管理器视图中拥有一个数据库,就像我一样(在本例中,它名为sql2k801.SQL2008_540970)。如果您还没有,请单击服务器资源管理器下的带加号图标的圆柱体以连接到数据库。
- 右键单击已连接数据库上的“表”,然后选择“添加新表”。按下面的说明填充表。在“属性”下,将表命名为 DemoTable。
- 将 `DataTable` 拖到DataClasses1.dbml上。
- 将 Key 设置为主键。
- 查看DataClasses1.dml的属性。将序列化模式设置为“单向”,以使其与 Web 服务兼容。
- 好了,数据库部分就完成了。现在我们来设置 Web 服务。右键单击 WcfSqlDemoWeb 项目,然后添加新项。选择“Silverlight 启用的 WCF 服务”模板,并将其命名为DemoService.svc。
- 删除 `DoWork` 方法,并替换为以下两个方法,以及以下 `
using
` 引用。顺便说一句,“`var selected_rows = from rows in db.DemoTables select rows
”这类代码就是 LINQ。它非常棒。它是 .NET 3.5 中内置的一种非常简洁且有意义的查询语言,可用于查询数据库、XML 甚至集合。理解 LINQ 的一种简单方法是查看一些示例。我推荐Microsoft 的 101 LINQ 示例。我在 `GetRows` 方法中使用的 LINQ 行大致意思是“获取表 db.DemoTable,并将该表中的元素分配给集合 rows,然后选择所有这些行并将它们分配给集合 selected rows”。 - 现在,我们需要让我们的 Silverlight 客户端知道我们刚刚创建的 Web 服务。右键单击 SilverlightClient,然后选择“添加服务引用”。单击“发现”。VS2008 应该能从项目中找到该服务。允许将其保存在命名空间 `ServiceReference1` 中。
- 在 Expression Blend 中打开page.xaml。创建一个看起来像我下面创建的表单。将文本框命名为 `TestItem1TxtBox` 和 `TestItem2TxtBox`。
- 现在,让我们添加 `DataGrid`,用于放置我们从 Web 服务检索到的数据。`DataGrid` 控件不是 Silverlight 项目的默认控件,因此您需要添加它。右键单击 SilverlightClient 中的“引用”并添加对 System.Windows.Control.Data 的引用。现在转到page.xaml。向用户控件元素添加以下属性,以便我们可以访问 `DataGrid`。
- 现在,您可以转到资源库,在“自定义控件”下找到 DataGrid。
- 向项目中添加一个 `DataGrid` 并调整其大小。将网格命名为 `ResultsGrid`。如果您查看 XAML,它应该与此类似:
- 添加一个按钮并将其标记为“获取”。最终结果应如下所示:
- 在 Expression Blend 中,将 `OnGetBtnClicked` 和 `OnSubmitBtnClicked` 添加到相应按钮的单击事件中。这将在 XAML 按钮标记中添加属性,并唤醒 VS2008,在代码隐藏文件中添加指定的方法。
- 按如下方式填充 `OnSubmitBtnClick` 和 `OnGetBtnClick`。创建回调来处理 `GetRowsCompleted` 事件。(顺便说一句,您是否注意到这有多容易?看看几行代码做了多少事情,再看看这些几行代码有多么简洁和有意义。)
- 生成并测试。尝试提交一些项目,然后获取结果。您应该会看到类似以下内容:
- 很简单,对吧?请注意,除了将结果分配给 `DataGrid` 的 `ItemsSource` 之外,我们无需做任何事情就能让 `DataGrid` 显示我们的结果。这种简单的唯一缺点是,我们看到所有返回的内容,包括代表键的 GUID。这不太用户友好。让我们删除列的自动生成,并创建自定义列。另外,我似乎需要为 `DataGrid` 添加显式大小,否则当网格为空时会出现奇怪的渲染。
- 生成并调试。结果应该与之前完全相同,只是这次没有“Key”列。
- 到目前为止,您的数据库中可能已经积累了一些垃圾数据,因为我们无法删除任何项目。现在让我们来解决这个问题。首先,让我们修改 Web 服务以能够从数据库中删除项目。将以下内容添加到 DemoService。请注意,我们使用了更多的 LINQ。下面用 LINQ 编写的行大致意思是“获取表 db.DemoTable,并将该表中的元素分配给集合 rows,然后选择那些键与传入的 GUID 匹配的行,并将这些选定的行分配给集合 selectedrow”。
- 添加一个删除按钮。当按下“删除”按钮时,它将删除 `DataGrid` 中选定的任何项目。
- 重新生成并调试。现在您可以选择项目并删除它们。
接下来您应该看到
using System.Collections.Generic;
using System.Text;
还有
// Return the list of valid data
[OperationContract]
public List<demotable> GetRows()
{
DataClasses1DataContext db = new DataClasses1DataContext();
var selected_rows = from rows in db.DemoTables select rows;
return selected_rows.ToList();
}
// Insert a new data into the database
[OperationContract]
public void InsertData(string testItem1,
string testItem2)
{
DataClasses1DataContext db = new DataClasses1DataContext();
// Create a new row object.
DemoTable row = new DemoTable
{
Key = Guid.NewGuid(),
TestItem1 = testItem1,
TestItem2 = testItem2
};
// Add the new object to the collection.
db.DemoTables.InsertOnSubmit(row);
// Submit the change to the database.
db.SubmitChanges();
}
xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
<my:DataGrid Margin="8,283,51,87" AutoGenerateColumns="True" x:Name="ResultsGrid"/>
private void OnGetBtnClick(object sender, RoutedEventArgs e)
{
DemoServiceClient webService = new DemoServiceClient();
webService.GetRowsCompleted +=
new EventHandler<getrowscompletedeventargs>(webService_GetRowsCompleted);
webService.GetRowsAsync();
}
void webService_GetRowsCompleted(object sender, GetRowsCompletedEventArgs e)
{
ResultsGrid.ItemsSource = e.Result;
}
private void OnSubmitBtnClick(object sender, RoutedEventArgs e)
{
DemoServiceClient webService = new DemoServiceClient();
webService.InsertDataAsync(TestItem1TxtBox.Text, TestItem2TxtBox.Text);
}
<my:datagrid margin="8,283,51,85"
autogeneratecolumns="False" x:name="ResultsGrid"
width="641" height="232">
<my:DataGrid.Columns>
<my:DataGridTextColumn
Header="Test Item 1"
DisplayMemberBinding="{Binding TestItem1}"/>
<my:DataGridTextColumn
Header="Test Item 2"
DisplayMemberBinding="{Binding TestItem2}"/>
</my:DataGrid.Columns>
</my:datagrid>
// Delete the item specified by the passed key
[OperationContract]
public void DeleteRow(Guid key)
{
DataClasses1DataContext db = new DataClasses1DataContext();
var selected_row = from rows in db.DemoTables where rows.Key==key select rows;
// Delete the selected "rows". There will actual be only one
// item in this collection because the Guid is unique and is the
// primary key for the table.
db.DemoTables.DeleteAllOnSubmit(selected_row);
// Submit the change to the database.
db.SubmitChanges();
}
private void OnDeleteClick(object sender, RoutedEventArgs e)
{
DemoTable selectedRow = ResultsGrid.SelectedItem as DemoTable;
// Now access the service to delete the item
DemoServiceClient webService = new DemoServiceClient();
webService.DeleteRowAsync(selectedRow.Key);
// Now refresh the grid
webService.GetRowsCompleted +=
new EventHandler<getrowscompletedeventargs>(webService_GetRowsCompleted);
webService.GetRowsAsync();
}
第二部分:将 Web 应用部署到远程服务器
- 将应用程序发布到 Web 可能有点困难。下面的说明已在我使用的 Web 主机提供商discountasp.net上进行了测试,但您很可能需要为其他主机提供商做类似的事情。
- 为了说明难度,发布网站。现在,DemoService 位于您的服务器上,对吧?您可能认为需要做的就是重新配置您的 Silverlight 客户端以引用您网站上的服务。继续尝试。右键单击 ServiceReference1,然后选择“配置服务引用”。添加服务的地址。在本例中,它是http://uiprototype.web702.discountasp.net/DemoService.svc。您将收到以下错误,并且无法继续。
- 这是解决方法。我们将覆盖服务器创建 Service Host 的方式。将以下类添加到DemoService.svc.cs
- 转到DemoService.svc,右键单击以查看标记,您会看到这个:
- 现在,重新生成所有项目并发布。返回 ServiceReference1,右键单击 ServiceReference1,然后选择“配置服务引用”。添加服务的地址。在本例中,它是http://uiprototype.web702.discountasp.net/DemoService.svc。现在它应该可以接受了。
public class MyServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType,
Uri[] baseAddresses)
{
// Specify the exact URL of your web service
Uri webServiceAddress =
new Uri("http://uiprototype.web702.discountasp.net/DemoService.svc");
ServiceHost webServiceHost = new ServiceHost(serviceType, webServiceAddress);
return webServiceHost;
}
}
<%@ ServiceHost Language="C#" Debug="true"
Service="WcfSqlDemoWeb.DemoService" CodeBehind="DemoService.svc.cs" %>
添加以下属性:
Factory="WcfSqlDemoWeb.MyServiceHostFactory"
第三部分:配置您的应用程序和远程服务器,以便您可以在各种本地和远程配置中进行调试
- 第二部分中描述的解决方法的唯一问题是您无法再进行本地调试。如果您现在尝试调试 Silverlight 页面并单击任一按钮(从而尝试访问 Web 服务),您将收到一个异常,说明找不到 Web 服务。这是由于跨域安全限制。调试时,您的客户端托管在 localhost 上,而 Web 服务托管在您的远程服务器上(在本例中为 discountasp.net)。当 localhost 托管的 Silverlight 客户端尝试连接到 DiscountAsp.net 托管的 Web 服务时,这是一个跨域通信,您需要明确授予对您的 Web 服务的此类访问权限。要授予此跨域访问权限,您需要在远程服务器上放置一个crossdomain.xml和一个clientaccesspolicy.xml文件。
- 如果您想回到从 localhost 调试客户端和 Web 服务,就像我们在本教程开头所做的那样,该怎么办?也许有更好的方法,但这是我所做的。转到您的Web.config文件,找到您的 `appSettings` 标签。将其替换为以下内容:
- 现在转到DemoService.svc.cs。添加以下 `
using
` 语句: - 右键单击 ServiceReference1,然后单击“配置服务引用”。将地址设置为https://:49797/DemoService.svc。
- 现在,重新生成所有项目并调试。情况应该像以前一样工作,只是现在 Web 服务又回到了本地托管。如果您不相信,请去服务器上删除所有内容。
- 现在,您可以通过更改 ServiceReference1 和Web.config中的地址来来回回切换。
- 就是这样!
这是您需要添加到 Web 项目中的crossdomain.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
这是您需要添加到 Web 项目中的clientaccesspolicy.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*" >
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
<appSettings>
<!--<add key="ServiceUri_WcfSqlDemo"
value="http://uiprototype.web702.discountasp.net/DemoService.svc"/>-->
<add key="ServiceUri_WcfSqlDemo" value="https://:49797/DemoService.svc"/>
</appSettings>
using System.Configuration;
在您的 `CreateServiceHost` 方法中,将 URI 更改为以下内容,以从Web.config文件中查找地址:
Uri webServiceAddress =
new Uri(ConfigurationManager.AppSettings["ServiceUri_WcfSqlDemo"]);