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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (17投票s)

2008年9月12日

CPOL

8分钟阅读

viewsIcon

247560

一个分步指南,介绍如何创建能够向数据库提交和检索数据的 Silverlight Web 应用。

您可以在我的网站上看到此完成的以数据为中心的 Silverlight 2.0 Web 应用程序的有效示例

引言

本文是一个分步教程,介绍如何创建以数据为中心的 Silverlight 2.0 Web 应用程序。本教程将让您对以下所有内容获得实际动手经验:

  • Silverlight 2.0 DataGrid
  • LINQ
  • Web 服务

如果您仔细阅读本教程,我认为您会对这三种技术是多么简单、强大和优雅感到惊讶。我真的开始觉得微软在这些新技术方面做得非常出色。

我还会为您提供有关如何将此应用程序部署到远程 Web 服务器的说明,在我看来,这可能是最困难的部分。我提供的说明特别适用于我的主机提供商discountasp.net;但是,无论您的托管提供商是谁,您可能都需要做类似的事情。

第一部分:构建应用程序并在本地进行测试

  1. 本教程需要您可以访问 Web 可访问的数据库。如引言中所述,我使用的是来自discountasp.net的 Web 托管帐户,带有 SQL Server 2008 附加组件,但也有许多托管提供商可供选择。要与远程数据库建立连接,请从菜单中选择“视图”,然后选择“服务器资源管理器”。单击“连接到数据库”选项,然后填写您的托管提供商提供的 SQL Server 名称和数据库登录名。单击“测试连接”以确认设置正确。
  2. 创建一个名为 SilverlightClient 的新 Silverlight 应用程序。将解决方案命名为 WcfSqlDemo。
  3. VS 2008 会询问您是否要将 Web 添加到解决方案。请选择“Web 应用程序项目”,而不是默认的“Web 站点”。将 Web 命名为 WcfSqlDemoWeb。
  4. 右键单击 WcfSqlDemoWeb,然后选择“添加新项”。选择“数据”->“LINQ to SQL 类”,并保留名称DataClasses1.dbml
  5. 接下来您应该看到

  6. 如果您在项目开始时设置了服务器,那么您已经在服务器资源管理器视图中拥有一个数据库,就像我一样(在本例中,它名为sql2k801.SQL2008_540970)。如果您还没有,请单击服务器资源管理器下的带加号图标的圆柱体以连接到数据库。
  7. 右键单击已连接数据库上的“表”,然后选择“添加新表”。按下面的说明填充表。在“属性”下,将表命名为 DemoTable。
  8. 将 `DataTable` 拖到DataClasses1.dbml上。
  9. 将 Key 设置为主键。
  10. 查看DataClasses1.dml的属性。将序列化模式设置为“单向”,以使其与 Web 服务兼容。
  11. 好了,数据库部分就完成了。现在我们来设置 Web 服务。右键单击 WcfSqlDemoWeb 项目,然后添加新项。选择“Silverlight 启用的 WCF 服务”模板,并将其命名为DemoService.svc
  12. 删除 `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”。
  13. 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();
    }
  14. 现在,我们需要让我们的 Silverlight 客户端知道我们刚刚创建的 Web 服务。右键单击 SilverlightClient,然后选择“添加服务引用”。单击“发现”。VS2008 应该能从项目中找到该服务。允许将其保存在命名空间 `ServiceReference1` 中。
  15. 在 Expression Blend 中打开page.xaml。创建一个看起来像我下面创建的表单。将文本框命名为 `TestItem1TxtBox` 和 `TestItem2TxtBox`。
  16. 现在,让我们添加 `DataGrid`,用于放置我们从 Web 服务检索到的数据。`DataGrid` 控件不是 Silverlight 项目的默认控件,因此您需要添加它。右键单击 SilverlightClient 中的“引用”并添加对 System.Windows.Control.Data 的引用。现在转到page.xaml。向用户控件元素添加以下属性,以便我们可以访问 `DataGrid`。
  17. xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
  18. 现在,您可以转到资源库,在“自定义控件”下找到 DataGrid。
  19. 向项目中添加一个 `DataGrid` 并调整其大小。将网格命名为 `ResultsGrid`。如果您查看 XAML,它应该与此类似:
  20. <my:DataGrid Margin="8,283,51,87" AutoGenerateColumns="True" x:Name="ResultsGrid"/>
  21. 添加一个按钮并将其标记为“获取”。最终结果应如下所示:
  22. 在 Expression Blend 中,将 `OnGetBtnClicked` 和 `OnSubmitBtnClicked` 添加到相应按钮的单击事件中。这将在 XAML 按钮标记中添加属性,并唤醒 VS2008,在代码隐藏文件中添加指定的​​方法。
  23. 按如下方式填充 `OnSubmitBtnClick` 和 `OnGetBtnClick`。创建回调来处理 `GetRowsCompleted` 事件。(顺便说一句,您是否注意到这有多容易?看看几行代码做了多少事情,再看看这些几行代码有多么简洁和有意义。)
  24. 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);
    }
  25. 生成并测试。尝试提交一些项目,然后获取结果。您应该会看到类似以下内容:
  26. 很简单,对吧?请注意,除了将结果分配给 `DataGrid` 的 `ItemsSource` 之外,我们无需做任何事情就能让 `DataGrid` 显示我们的结果。这种简单的唯一缺点是,我们看到所有返回的内容,包括代表键的 GUID。这不太用户友好。让我们删除列的自动生成,并创建自定义列。另外,我似乎需要为 `DataGrid` 添加显式大小,否则当网格为空时会出现奇怪的渲染。
  27. <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>
  28. 生成并调试。结果应该与之前完全相同,只是这次没有“Key”列。
  29. 到目前为止,您的数据库中可能已经积累了一些垃圾数据,因为我们无法删除任何项目。现在让我们来解决这个问题。首先,让我们修改 Web 服务以能够从数据库中删除项目。将以下内容添加到 DemoService。请注意,我们使用了更多的 LINQ。下面用 LINQ 编写的行大致意思是“获取表 db.DemoTable,并将该表中的元素分配给集合 rows,然后选择那些键与传入的 GUID 匹配的行,并将这些选定的行分配给集合 selectedrow”。
  30. // 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();
    }
  31. 添加一个删除按钮。当按下“删除”按钮时,它将删除 `DataGrid` 中选定的任何项目。
  32. 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();
    }
  33. 重新生成并调试。现在您可以选择项目并删除它们。

第二部分:将 Web 应用部署到远程服务器

  1. 将应用程序发布到 Web 可能有点困难。下面的说明已在我使用的 Web 主机提供商discountasp.net上进行了测试,但您很可能需要为其他主机提供商做类似的事情。
  2. 为了说明难度,发布网站。现在,DemoService 位于您的服务器上,对吧?您可能认为需要做的就是重新配置您的 Silverlight 客户端以引用您网站上的服务。继续尝试。右键单击 ServiceReference1,然后选择“配置服务引用”。添加服务的地址。在本例中,它是http://uiprototype.web702.discountasp.net/DemoService.svc。您将收到以下错误,并且无法继续。
  3. 这是解决方法。我们将覆盖服务器创建 Service Host 的方式。将以下类添加到DemoService.svc.cs
  4. 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; 
        }
    }
  5. 转到DemoService.svc,右键单击以查看标记,您会看到这个:
  6. <%@ ServiceHost Language="C#" Debug="true" 
        Service="WcfSqlDemoWeb.DemoService" CodeBehind="DemoService.svc.cs" %>

    添加以下属性:

    Factory="WcfSqlDemoWeb.MyServiceHostFactory"
  7. 现在,重新生成所有项目并发布。返回 ServiceReference1,右键单击 ServiceReference1,然后选择“配置服务引用”。添加服务的地址。在本例中,它是http://uiprototype.web702.discountasp.net/DemoService.svc。现在它应该可以接受了。

第三部分:配置您的应用程序和远程服务器,以便您可以在各种本地和远程配置中进行调试

  1. 第二部分中描述的解决方法的唯一问题是您无法再进行本地调试。如果您现在尝试调试 Silverlight 页面并单击任一按钮(从而尝试访问 Web 服务),您将收到一个异常,说明找不到 Web 服务。这是由于跨域安全限制。调试时,您的客户端托管在 localhost 上,而 Web 服务托管在您的远程服务器上(在本例中为 discountasp.net)。当 localhost 托管的 Silverlight 客户端尝试连接到 DiscountAsp.net 托管的 Web 服务时,这是一个跨域通信,您需要明确授予对您的 Web 服务的此类访问权限。要授予此跨域访问权限,您需要在远程服务器上放置一个crossdomain.xml和一个clientaccesspolicy.xml文件。
  2. 这是您需要添加到 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>
  3. 如果您想回到从 localhost 调试客户端和 Web 服务,就像我们在本教程开头所做的那样,该怎么办?也许有更好的方法,但这是我所做的。转到您的Web.config文件,找到您的 `appSettings` 标签。将其替换为以下内容:
  4. <appSettings>
        <!--<add key="ServiceUri_WcfSqlDemo" 
           value="http://uiprototype.web702.discountasp.net/DemoService.svc"/>-->
        <add key="ServiceUri_WcfSqlDemo" value="https://:49797/DemoService.svc"/>
    </appSettings>
  5. 现在转到DemoService.svc.cs。添加以下 `using` 语句:
  6. using System.Configuration;

    在您的 `CreateServiceHost` 方法中,将 URI 更改为以下内容,以从Web.config文件中查找地址:

    Uri webServiceAddress = 
      new Uri(ConfigurationManager.AppSettings["ServiceUri_WcfSqlDemo"]);
  7. 右键单击 ServiceReference1,然后单击“配置服务引用”。将地址设置为https://:49797/DemoService.svc
  8. 现在,重新生成所有项目并调试。情况应该像以前一样工作,只是现在 Web 服务又回到了本地托管。如果您不相信,请去服务器上删除所有内容。
  9. 现在,您可以通过更改 ServiceReference1 和Web.config中的地址来来回回切换。
  10. 就是这样!
© . All rights reserved.