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

使用 Code First 方法为 Silverlight 应用程序创建 WCF RIA Services 类库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (12投票s)

2012 年 3 月 26 日

CPOL

8分钟阅读

viewsIcon

88258

downloadIcon

3551

为 Silverlight 应用程序创建和配置 WCF RIA Services 类库

引言

WCF RIA Services 类库组件是任何需要清晰分离中间层的 Silverlight 应用程序的首选选项。其架构和提供的优势在 MSDN 文档中有详细描述。此外,还有一个关于使用 ADO.NET Entity Framework 数据库优先方法创建 RIA Services 类库的演练。在使用代码优先方法实现 RIA Services 类库项目时,情况会有所不同,并且没有详细的文档可用。本文将提供一个关于如何创建和配置代码优先 RIA Services 类库项目的完整分步教程。

所需工具和组件

创建新的或修改现有的 Silverlight 应用程序

如果您创建一个新的 Silverlight 应用程序解决方案,请遵循 MSDN 中的此演练中的步骤,但在“新建 Silverlight 应用程序”对话框中不要勾选“启用 WCF RIA Services”复选框。

如果您将 RIA Services 类库添加到具有默认结构的现有 Silverlight 应用程序中(RIA Services 驻留在 Silverlight Web 服务器项目中),请通过在 Silverlight 客户端项目的“属性”屏幕的“Silverlight”选项卡中,在“WCF RIA Services 链接”下拉列表中选择“<未设置项目>”来从现有应用程序解决方案中删除 RIA Services 链接。

1.png

Silverlight 应用程序不需要客户端项目和服务器项目之间的 RIA Services 链接,因为 RIA Services 链接仅存在于类库的项目之间,如图MSDN 文档所示。在本教程中,Silverlight 应用程序项目命名为 `ProductApp` 和 `ProductApp.Web`。

添加 RIA Services 类库项目

2.png

这会在 `ProductRiaLib` 解决方案虚拟文件夹下创建 `ProductRiaLib`(服务客户端代理)和 `ProductRiaLib.Web`(服务服务器项目)。我们可以使用单独的解决方案文件夹组来重新排列项目,这不会改变物理目录结构。在本教程中,我们保留该虚拟解决方案文件夹。创建 RIA Services 类库项目后,RIA Service 链接已自动添加到服务器和客户端代理项目之间。

3.png

  1. 在解决方案资源管理器中右键单击解决方案,选择“添加”,然后选择“新建项目”。
  2. 在“Silverlight”类别下的“已安装模板”中,选择“WCF RIA Services 类库”模板,并将其命名为 `ProductRiaLib`。
  3. 删除服务器和客户端代理库项目中默认的Class1.cs文件。初始解决方案应如下所示。

设置实体框架和数据库上下文

4.png

实体框架列在“所有”部分的“在线”侧边菜单中。点击“安装”按钮开始包安装过程。按照提示完成实体框架 4.3.1 的安装。您可以通过在“程序包管理器控制台”中执行命令来安装实体框架,特别是当您需要不同版本的实体框架时。详细信息请参阅NuGet网站。

5.png

Product.cs 文件包含数据实体类

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ProductRiaLib.Web.Models
{
    public class Product
    {
        public int ProductID { get; set; }
        public string ProductName { get; set; }
        public int? CategoryID { get; set; }
        public Decimal? UnitPrice { get; set; }                
        public bool OutOfStock { get; set; }
        
        public virtual Category Category { get; set; }
    }

    public class Category
    {
        public int CategoryID { get; set; }
        public string CategoryName { get; set; }     

        public virtual ICollection<Product> Products { get; set; }
    }
}

ProductDBContext.cs 文件包含 `ProductDbContext` 类,用于创建数据库上下文,以及 `ProductDbContextInitializer`,用于填充测试数据。在此示例中,测试数据填充在实例化 `ProductDbContext` 类时进行初始化。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.ComponentModel.DataAnnotations;

namespace ProductRiaLib.Web.Models
{
    public class ProductDbContext : DbContext
    {
        public ProductDbContext()
        {
            if (HttpContext.Current != null)
            {
                Database.SetInitializer<ProductDbContext>(new ProductDbContextInitializer());
            }
        }

        public DbSet<Product> Products { get; set; }
        public DbSet<Category> Categories { get; set; }
        
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }

    public class ProductDbContextInitializer : 
                 DropCreateDatabaseIfModelChanges<ProductDbContext>    
    {
        protected override void Seed(ProductDbContext context)
        {
            Category[] categories = new Category[]
            {
                new Category { CategoryID = 1, CategoryName = "Bath",
                    Products = new Product[] { 
                        new Product { ProductID = 1, 
                                      ProductName = "Bath Rug", UnitPrice = 24.5m},
                        new Product { ProductID = 2, ProductName = "Soap Dispenser", 
                                      UnitPrice = 12.4m, OutOfStock = true},                        
                        new Product { ProductID = 3, ProductName = "Shower Curtain", 
                                      UnitPrice = 30.99m},
                        new Product { ProductID = 4, ProductName = "Toilet Tissue", 
                                      UnitPrice = 15},
                    }.ToList()},
                new Category { CategoryID = 2, CategoryName = "Bedding",
                    Products = new Product[] { 
                        new Product { ProductID = 5, 
                                      ProductName = "Branket", UnitPrice = 60},
                        new Product { ProductID = 6, ProductName = "Mattress Protector", 
                                      UnitPrice = 30.4m, OutOfStock = true },
                        new Product { ProductID = 7, ProductName = "Sheet Set", 
                                      UnitPrice = 40.69m},
                    }.ToList()},
                new Category { CategoryID = 3, CategoryName = "kitchen",
                    Products = new Product[] { 
                        new Product { ProductID = 8, ProductName = "Baking Pan", 
                                      UnitPrice = 10.99m},
                        new Product { ProductID = 9, ProductName = "Coffee Maker", 
                                      UnitPrice = 49.39m},
                        new Product { ProductID = 10, ProductName = "Knife Set", 
                                      UnitPrice = 70},
                        new Product { ProductID = 11, ProductName = "Pressure Cooker", 
                                      UnitPrice = 90.5m, OutOfStock = true },
                        new Product { ProductID = 12, ProductName = "Water Pitcher", 
                                      UnitPrice = 29.99m},
                    }.ToList()},
            };
            foreach (var category in categories)
            {
                context.Categories.Add(category);
            }
            context.SaveChanges();
        }
    }
}

如代码所示,数据库上下文初始化和数据填充仅在链接的 Silverlight 应用程序网站发出 HTTP 请求运行时发生。

  1. 在 `ProductRiaLib.Web` 项目的“引用”上右键单击,然后选择“管理 NuGet 程序包…”。
  2. 在 `ProductRiaLib.Web` 服务服务器项目中添加一个名为“Models”的文件夹,然后向该文件夹中添加两个文件。
  3. 如果 `ProductRiaLib.Web` 项目中不存在 `System.Web` 程序集引用,请添加它。
  4. Ctrl+Shift+B 生成解决方案。这将使数据库上下文可供将在下一节中创建的域服务使用。

添加域服务类并修改配置

域服务通过 CRUD(创建、读取、更新、删除)方法和强制执行业务逻辑,将类库服务器项目中的数据实体和操作公开给客户端代理。

  1. 在 `ProductRiaLib.Web` 服务服务器项目中添加两个名为“App_Data”和“Services”的文件夹。
  2. 右键单击“Services”文件夹,选择“添加”,然后选择“新建项”。在“添加新项”对话框中,从“已安装模板”中选择“Web”,然后选择“域服务类”。
  3. 将文件名设置为 ProductDomainService.cs,然后点击“添加”。
  4. 在“添加新域服务类”对话框中,勾选“Category”和“Product”复选框,同时勾选两个“启用编辑”复选框,然后点击“确定”。请注意,如果实体列表为空,您可以关闭并重新打开 Visual Studio 中的解决方案,重新生成解决方案,然后重试。

    6.png

    ProductDomainService.cs 文件将在Services文件夹中创建,同时向项目添加了三个额外的引用:`System.ServiceModel.DomainServices.Hosting`、`System.ServiceModel.DomainServices.Server` 和 `Microsoft.ServiceModel.DomainServices.EntityFramework`。解决方案中的服务类库服务器项目现在如下所示。

    7.png

    此时,如果重新生成解决方案,类库中的 `Generated_Code\ProductRiaLib.Web.g.cs` 客户端代理文件夹和文件应该会被创建。这仅适用于使用数据库优先方法创建的 RIA Services 类库,而不适用于代码优先方法。问题是由与App.config文件相关的一些设置引起的。我们需要调整配置以使其正常工作。

  5. App.config文件的最后一个 `</configuration>` 标签之前,添加以下代码行。尽管我们使用了通过 NuGet 新安装的实体框架 4.3.1,但其他支持程序集较旧,并且与代码优先方法不兼容。
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <dependentAssembly>
            <assemblyIdentity name="EntityFramework" 
               publicKeyToken="b77a5c561934e089" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-4.3.1.0" newVersion="4.3.1.0" />
          </dependentAssembly>
        </assemblyBinding>
    </runtime>

    重新生成解决方案后,客户端代理文件仍然未生成。

  6. App.config重命名为Web.config并重新生成解决方案。现在客户端代理文件已成功生成。在类库服务器项目中,数据库优先方法可以使用App.config文件,但代码优先方法不行。这可能是由于实体框架/RIA Services 中存在一个 bug,即代码无法在最终合并到machine.config时找到App.config文件名。但是,`Web.config` 文件名存在于可搜索的树中。

使用服务类库提供的数据

  1. 将 `ProductRiaLib.Web\Web.config` 文件中的 `<system.webServer>`、`<system.serviceModel>`、`<httpModules>`(位于 `<system.web>` 下)以及 `<runtime>` 节点复制到 Silverlight 应用程序服务器项目 `ProductApp.Web\Web.config` 中。
  2. 向 `ProductApp.Web\Web.config` 添加数据库连接字符串。 `ProductApp.Web\Web.config` 文件中的最终内容如下所示。您需要将斜体部分替换为您数据库文件的位置。
    <configuration>
      <connectionStrings>
        <add name="ProductDbContext" 
           connectionString="Data Source=[Your-ProductRiaLib.web-Path]\
                  App_Data\ProductData.sdf" providerName="System.Data.SqlServerCe.4.0" />
    </connectionStrings>
      <system.web>
        <compilation debug="true" targetFramework="4.0" />
        <httpModules>
          <add name="DomainServiceModule" 
             type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, 
                   System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, 
                   Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        </httpModules>
      </system.web>
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true">
          <add name="DomainServiceModule" preCondition="managedHandler" 
             type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, 
                   System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, 
                   Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        </modules>
        <validation validateIntegratedModeConfiguration="false" />
      </system.webServer>
      <system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" 
         multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
      <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <dependentAssembly>
            <assemblyIdentity name="EntityFramework" 
             publicKeyToken="b77a5c561934e089" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-4.3.1.0" newVersion="4.3.1.0" />
          </dependentAssembly>
        </assemblyBinding>
      </runtime>
    </configuration>

    如果您想连接到完整的 SQL Server 实例或 SQL Server Express,只需根据您的选择更改连接字符串即可。

    SQL Server 2008 R2 的连接字符串

    <add name="ProductDbContext" 
      connectionString="Server=[YourSQLServerInstanceName];Database=ProductData;
         Integrated Security=True;" providerName="System.Data.SqlClient" />

    SQL Server Express 2008 R2 的连接字符串

    <add name="ProductDbContext" 
       connectionString="Data Source=.\SQLExpress;Integrated Security=SSPI; 
         Database=ProductData; AttachDBFilename=[Your-ProductRiaLib.web-Path]\
         App_Data\ProductData.mdf; User Instance=true" 
         providerName="System.Data.SqlClient" />

    如果未指定连接字符串,实体框架将使用内置的 `defaultConnectionFactory` 设置在本地 SQL Server Express 实例(如果存在)中创建数据库,数据库名称为“ProductRiaLib.Web.Models.ProductDbContext”。大多数人实际上不喜欢这种行为。

    如果您使用 SQL Server Compact 数据库,并且没有安装 SQL Server Express 版本或已停止 SQL Server Express 服务,则在运行应用程序创建数据库文件和初始化数据上下文时,可能会遇到“Failed to get the MetadataWorkspace for the DbContext type”错误。默认情况下,即使您删除了 `defaultConnectionFactory` 设置,代码优先的 RIA 域服务仍会使用或引用 SQL Server Express 来获取元数据工作区。如果发生这种情况,您可以在 `ProductRiaLib.Web` 项目的Web.config文件中添加一个具有与 `name` 属性相同值的虚拟连接字符串。

    <connectionStrings>
        <add name="ProductDbContext" connectionString="" 
         providerName="System.Data.SqlServerCe.4.0" />
    </connectionStrings>
  3. 为了检查类库中服务器端进行的任何更改是否能在客户端得到反映,只需添加一个函数,该函数根据选定的类别返回产品列表。将代码行添加到 `ProductRiaLib.Web\ProductDomainService.cs` 文件中的 `GetProducts()` 方法下方。
    public IQueryable<Product> GetCategorizedProduct(int categoryId)
    {
        return this.DbContext.Products.Where(
          e => e.CategoryID == categoryId).OrderBy(e => e.ProductName);
    }
  4. 重新生成解决方案。确认新添加的方法 `GetCategorizedProductQuery()` 可从类库客户端代理的Generated_Code\ProductRiaLib.Web.g.cs中获取。
  5. 要完成使用 RIA Services 提供的数据测试 Silverlight 客户端项目,请使用文章开头显示的链接下载源代码文件。如果您已创建自己的解决方案,可以将某些文件合并到现有的 `ProductApp` 项目中,并解决任何缺失的 .NET 程序集引用。
    • Assets\Styles.xaml
    • Views\ProductList.xaml 及其 cs 文件
    • Views\AddProductWindow.xaml 及其 cs 文件
    • Views\ErrorWindow.xaml 及其 cs 文件
    • App.xaml 及其 cs 文件
  6. 确保将 `ProductApp.Web` 项目设置为启动项目。按 F5 运行应用程序。将呈现“产品列表”页面,并在从下拉列表中选择任何类别时,数据将在网格中显示。您可以编辑和删除产品数据。还有一个子窗口用于添加新产品。

    8.png

    9.png

    10.png

  7. 运行应用程序后,数据库应会自动创建。下方以 SQL Server Compact 4.0 为例进行展示。我们可以使用 Visual Studio 服务器资源管理器,通过双击 `ProductRiaLib.Web\App_Data` 文件夹中的ProductData.sdf文件来检查数据库中的表和数据。

    11.png

摘要

代码优先方法在数据密集型应用程序开发中正变得越来越流行。使用此方法创建 WCF RIA Services 类库,有助于为任何规模的 Silverlight 应用程序构建真正的中间层和可移植组件。希望本分步教程对需要这些详细信息的开发人员有所帮助。

历史

  • 2012 年 3 月 26 日:初始版本
© . All rights reserved.