使用 SilverLight 4 业务应用程序开发模板 (LOB)






4.60/5 (4投票s)
使用 SilverLight 4 LOB 开发,结合 RIA Services、DTO 和 EntityFramework
引言
多年前,我曾是一名 Windows Forms 分析师/开发人员,鉴于向 Web 应用程序开发的趋势转变,我正在应用程序开发的精彩世界中寻找新的挑战。在接触了 ASP.NET 和 ASP.NET MVC 框架一段时间后,我最终决定专注于开发 Silverlight LOB 应用程序。
本文将向中级和高级 Silverlight 开发人员介绍 Silverlight 的概念,如 RIA Services、DTO(数据传输对象)、创建 DataTemplates、ValueConverters 以及使用 DomainDataSource。
背景
本文并非旨在创建功能齐全的 SilverLight LOB 应用程序,包括 MVVM 模式、业务规则验证、样式等,而是侧重于传输 DTO 作为轻量级对象,以展示一个简单的只读自行车网店列表。DTO,即数据传输对象,是所谓的“轻量级”POCO(纯 CLR 对象),它们包含将数据从服务器端 DomainService 传输到客户端所需的最小信息(属性)。在我们的演示案例中,DTO 对象将填充自行车信息,并将成为绑定到ListBox 的 ItemsSource。
解释应用程序创建步骤。
阅读本文的人员应该已经对 Silverlight LOB 应用程序的构建方式有了一定的了解,因为我不会解释这些细节。如果您不熟悉这些概念,请先参考主要的 Silverlight 网站,您可以通过激活以下 URL 找到它:
服务器端
我将逐步构建应用程序,首先解释底层数据库,创建 Entity Framework 模型,创建包含自行车信息的 DTO,以及创建用于将自行车信息加载到 DTO 列表中的域服务。这些元素是项目中包含的网站项目的一部分:
演示数据库包含在项目中的/App_Data 文件夹下,名为MountainBikeDB.mdf(附加的数据库文件)。该数据库包含一个名为 bike 的主表,其中包含主要的自行车属性;其他相关表包含适用于每种自行车类型的数,例如车架类型、前叉类型、刹车类型、自行车类型等。关系结构如下:
稍后将解释的DomainService 持有一个指向EntityFrameWork 上下文的引用,该上下文派生自我们的附加 SQL Server 数据库。
我们应用程序的目的是显示一个包含自行车信息的自行车摘要列表。为此,一组只读对象就足够了。因此,我们不需要可编辑的对象(我们只暴露 CRUD 首字母缩写中的 R,C=Create,R=Read,U=Update,D=Delete),我们更倾向于使用轻量级对象将数据传输到客户端。此轻量级对象将存储在 WebProject 的/Models 文件夹中,它是一个简单的类,带有一些用于 DomainService 的注解(例如 [key] 属性,用于唯一标识列表中的每个对象。
namespace SilverlightLOB_RIA_DTO.Web.Models
{
public partial class BikeDTO
{
[Key]
[Editable(false)]
public int BikeID { get; set; }
public string BikeName { get; set; }
public byte[] BikeImage { get; set; }
public decimal ModelNumber { get; set; }
public string FrameType { get; set; }
public string BikeType { get; set; }
public string ForkType { get; set; }
public string BrakeType { get; set; }
public string GroupSet { get; set; }
public string WheelSet { get; set; }
public int? InStock { get; set; }
}
}
请注意,BikeID
属性带有关联的 [Key]
和 [Editable(false)
] 属性。原因在于用户不应被允许更新该字段,因为它是一个服务器端生成的自动增量键。第二个原因是 [Key] 注解是因为 RIA-DomainService 需要它,因为每个对象都必须能够被唯一标识。
最后,由于我们只会在 SilverLight 客户端应用程序中公开一个只读自行车列表,因此我们可以为每个属性加上 [Editable(false]
注解,但如果我们要将此 DTO 重用于在另一个视图中执行其他更新,我们就没有这样做。
接下来是我们的 DomainService
,它位于我们 WebProject 的/Services 文件夹中,公开 1 个方法,该方法从我们的 Entity Framework Context 读取自行车信息,并为每个对象创建一个 DTO 对象。最后,将这些对象的列表返回到我们应用程序的客户端部分(Silverlight 应用程序)。该服务是所谓的Domain Service Class 服务,其实现如下所示。
[EnableClientAccess()]
public class BikeService : LinqToEntitiesDomainService<MountainBikeDBEntities>
{
public IQueryable<BikeDTO> GetBikeDTOList()
{
return from bike in this.ObjectContext.Bikes
orderby bike.BikeName
select new BikeDTO
{
BikeID = bike.BikeID,
BikeName = bike.BikeName,
BikeImage = bike.Photo,
ModelNumber = bike.ModelNumber,
FrameType = bike.FrameType.FrameTypeName,
BikeType = bike.BikeType.BikeTypeName,
ForkType = bike.ForkType.ForkTypeName,
BrakeType = bike.BrakeType.BrakeTypeName,
GroupSet = bike.GroupSet.GroupSetName,
WheelSet = bike.WheelSet.WheelSetName,
InStock = bike.InStock
};
}
}
就是这样!这就是我们服务器端的工作,现在让我们继续解释客户端部分!
客户端通用概念
一句话...
在不详细介绍 Silverlight LOB 应用程序模板的概念的情况下,您可能已经注意到我们的解决方案 ghost 了两个项目,即 WebPart(SilverlightLOB_RIA_DTO.Web)和 ClientPart(SilverlightLOB_RIA_DTO)。Web 部分包含客户端部分(Silverlight)的编译版本,形式为 .xap 文件。另一方面,客户端部分持有对 DomainServices 相关 DLL 的引用(System.ServiceModel.DomainServices.*)。
这些引用的组合使得两个层之间能够进行通信。另外请注意(如果启用了“显示所有文件”)您可以在客户端浏览到名为Generated Code 的隐藏文件夹,该文件夹包含我们的DTO 数据传输对象的定义等。
当您向服务器端部分的 DTO 对象添加属性或通过元数据类添加验证规则,然后重新编译解决方案时,客户端上的 Generated_Code 部分也将反映这些更改。再次强调,本文的目的不是解释 Silverlight LOB 模板的详细信息,但了解客户端的模型反映服务器创建的模型很重要!
WebSite 上的 .xap 文件包含完整的 SilverLight 应用程序(以压缩形式)。当应用程序部署到 Web 服务器时,在第一次客户端请求时,.xap 文件将被发送到客户端并解压缩。WebSite 在部署客户端部分之前还会检查 SilverLight 运行时是否已安装,如果没有,则会通知用户将首先安装 SilverLight 运行时。
好的,理论够多了,现在让我们看看客户端如何获取数据并将其显示在自行车 ListBox 中!
客户端基础设施概念
我已经在 .NET 领域摸爬滚打了一段时间了,早在 2003 年就用 C# Visual Studio Standard 2003 开始了。如今,我们被有关基础设施模式的信息淹没了,创建 n 层(层)应用程序是趋势,单体编程如今等同于糟糕的设计。当我开始接触 .NET 时,并没有太多关于将表示层与业务层和数据访问层分离的内容。MSDN 上的大多数分步教程都向我们展示了 RAD(快速应用程序开发),或者我应该说 Q 和 D(快速且粗糙)的方法,将DataSources 拖放到 Windows 窗体上,这会生成TableAdapters、BindingSources、BindingNavigators 等(至少在 VS2005 中引入了类型化数据集)。起初这真是“哇,看啊,没有代码!”……但一切都被推到了表示层,导致应用程序维护困难。为了展示这个概念,我在解决方案中添加了一个名为BikeAdmin 的小型演示应用程序,您可以花些时间研究一下,看看单体应用程序设计是什么样的。事实上,TableAdapterManager 应该从用户界面中移除,并放入它自己的类中,即DataService 类。接下来,我们还应该为我的 Bike 添加一个BusinessClass,该类从DataService 请求数据,添加业务规则(使用类型化数据集,您可以使用Column_Changing/Changed、Row_Changing/Changed……事件处理程序将您的业务逻辑“挂钩”进去)。但是,与 SilverLight Business Template 不同,您必须自己创建框架。在转向 SilverLight 客户端实现之前,让我们快速回顾一下在“旧的 Windows 窗体时代”如何实现这种n 层分离!我添加了一个 Windows 窗体项目来展示这一点,它被称为BikeAdmin。
单体方法,拖放 DataSource
如果 BikeAdmin 项目中的FormBike 代表了“窗体包含一切”的方法。只需拖放 Bike 数据集的 DataSource,就会生成大量代码来捕获数据,挂接数据绑定以及加载/保存数据。不幸的是,所有内容都放在了“Code Behind”文件中。如果您查看此窗体中的代码,您会发现在窗体的代码隐藏文件中创建了 TableAdapters,并且所有数据相关的问题都在此处处理。这样,您可以快速启动并运行您的窗体,但它缺乏代码重用、关注点分离、代码可维护性和单元测试。
分层开发方法(n 层)
第二种方法,如FormGetBikeList 中所示,将TableAdapters 放在DataServices 类中,该类向 BusinessClass 公开公共方法。BusinessClass 可以扩展业务规则,并作为数据访问层和表示层之间的网关。有关详细信息,请参阅代码。
客户端 SilverLight 应用程序
原型制作
由于我们的数据将在具有相当复杂结构的 ListBox 中显示,因此最好在单独的页面中原型化“ListItem”的外观(或者使用 Expression Blend,这是一个很棒的设计师工具,但不在本文的讨论范围内)。
因此,当您将“一组”对象绑定到 ListBox 时,ListBox 不知道要绑定到哪里,并将绑定到 ObjectStructure 本身(如果未实现 ToString()
方法的重写)。我们 ListBox 中的每个项都应代表自行车信息。ListBox 公开一个 ItemTemplate
,在 Itemtemplate 中您可以定义一个 DataTemplate,然后该 DataTemplate 可以存储为 StaticResource
(在包含的页面中或在多功能字典中),并应用于我们的 ListBox。我首先在一个名为 /ProtoType 的单独文件夹中设计了这个 DataTemplate,并用静态数据加载了它。结果如下所示。
我不会详细介绍 XAML 代码,但请花些时间了解模板是如何构建的。最重要的是,一旦创建了包含列表的窗体,我就会将模板从原型中复制/粘贴,并将其添加为页面的 Static Resource。最后,我将用 DomainDataSource 公开的 DataContext 的Binding 元素替换静态数据。
DomainDataSource
在 SilverLight 中,您有许多方法可以从服务器获取数据并将其绑定到客户端。在本演示中,我们将展示DomainDataSource 方法,您可以将其与早期 Windows Forms 技术中的 DataSource Drag/Drop 功能进行比较。是的,这次我们将采取快速粗糙的方式,但这对于我们的目的来说是完全可以的,即显示一个包含自行车信息的只读 ListBox。请注意,如果您想开发真正专业且易于维护的 SilverLight LOB 应用程序,包括 CRUD 操作,那么您应该避免使用 XAML 绑定的 DomainDataSource 方法,而应实现 MVVM(Model-View-ViewModel)模式,该模式鼓励关注点分离、代码重用和单元测试。尽管有很多方法可以实现 SilverLight 中的 MVVM 模式,并且有许多不同的观点和辅助框架,但目前我们跳过这一部分,专注于使用DomainDataSource 方法直接在 XAML 页面中绑定数据!
在我们的案例中,我在/Views 文件夹中创建了一个新的 SilverLight Page,名为BikeListView.xaml。此页面将包含我们的自行车列表,因此我们必须将我们的 DataSource“拖动”到页面的画布上。首先(打开 xaml 页面时)打开 DataSource 工具箱。
下一个工具箱将出现在屏幕的左侧。
最后,将 BikeDTO DataSource 拖放到当前 xaml 页面的表面上(即包含我们 ListBox 的页面)。
接下来将添加与 RIA Service 相关的引用到页面顶部。
这些与将 WCF 服务引用添加到客户端应用程序相当。
拖放操作还将导致在 XAML 代码中添加 DomainDataSource Proxy,在本例中是加载自行车数据到 ListBox 的那个。
被红色框包围的代码是手动添加的过滤和排序选项,因为我们希望列表默认按 bikename 排序,并按 biketype(越野 / 泥地 / 速降 / 自由骑行)进行过滤,这些都是有价值的过滤选项。
让我们花些时间来解释生成的 XAML 代码的各个方面。
- AutoLoad 属性设置为 True,这意味着视图加载后将立即调用服务器。
- 已分配事件处理程序来处理 LoadedData 事件。此处理程序将加载我们的自行车 DTO 对象。
- QueryName 属性设置为您要在域服务上调用的查询域操作的名称。
- DomainContext 属性被分配了一个域上下文实例,该实例将负责从服务器获取实体集合。
SearchTextBox
页面顶部的搜索文本框允许按 biketype 进行过滤。文本框的 ElementName 绑定到我们 DTO 的 "BikeType" 属性的 PropertyPath。
并且 Operator 默认为使用 'StartsWith' 子句搜索 biketypes。
ListBox 和 DataPager
我们的 ListBox 包含 ListBoxItems,其 ItemTemplate 等于指向 _bikeItemTemplate 的 StaticResource(有关更多信息,请参阅完整的 XAML 代码)。
我们 ListBox 的 ItemsSource 指向我们 DomainService 的 DataLoader 方法,Path 指示 Data,在本例中是我们从服务器返回的自行车 DTO 对象列表。
虽然我们让 DomainService 通过单次调用加载 100 个 DTO,但我们创建了一个 DataPager 来成对地导航它们。
它还有一个 IsTotalItemCountFixed
属性,如果设置为 true,将阻止用户导航到超出计算页数(通过禁用“下一页”按钮)。
如果您检查 XAML 代码,您还会注意到一个“IsBusyIndicator
”,它会在从服务器加载数据时显示一个进度条。
结语
好了,希望您喜欢这篇关于 SilverLight LOB 技术的文章。只需运行项目即可看到主页出现(在照片中,是我自己和我最小的儿子,点击 BikeListView 按钮即可加载 bikelistbox……)。还有很多工作要做,比本文介绍的要多。Silverlight 4 LOB 模板真正准备好构建一致的业务线应用程序,包括验证、事务、实现 MVVM 模式以使团队相关项目更容易访问。对我来说,Silverlight 有着光明的未来,越来越多的人正在接受它,不仅作为 Adobe Flash 的替代品或选择,而且它在我们之间的 Symbiose 很好地结合了 ASP.NET 和 Windows 桌面开发。起初,要理解 SilverLight LOB 中的所有新技术,学习曲线相当陡峭,但一旦您深入其中,我敢肯定您将不再回头看您的旧平台(ASP.NET 或 WinForms)。对我个人而言,我将继续探索我在 SilverLight LOB 方面的知识,并希望尽快在专业上转向这个平台!