Silverlight 2 数据库更新






4.08/5 (7投票s)
从 Silverlight 应用程序执行数据库更新。
引言
本文演示了从 Silverlight 2 应用程序更新数据库的方法。
随着最近 Silverlight 2 Beta 2 的发布,访问和操作数据库数据变得更加容易。由于 Silverlight 应用程序无法直接访问本地资源,因此数据访问是通过 Web 服务(如 Windows Communications Foundation (WCF) 或 ASMX)提供的。有许多示例演示了如何读取数据并填充数据对象,但很少有示例提供将更改写回数据源的任何功能。
在这里找到的一个示例 此处 使用了 ADO.NET Data Services (Astoria),该服务仍在开发中。
另一个解决方案,在 此处 找到,使用了 LINQ,并复制了原始数据。更新时,它会将原始数据和修改后的数据集都发送回服务器。因此,如果您有 50 行数据,您最多会发送 100 行数据。虽然这种方法有效,但我不喜欢即使只想做一些小的更改也要发送大量数据的想法。该页面包含一个视频,其源代码绝对值得一看。
本文演示了一种仅发送更改回数据库的方法。它假定您具备一些基本的 Silverlight 2 操作知识,例如调用 Web 服务和将数据绑定到控件。文章《我的第一个 Silverlight 数据项目》为此类操作提供了良好的介绍。另一个资源是 Silverlight 入门页面。
源代码使用 Visual Studio 2008 和 Silverlight 2 Beta 2 中的 C# 编写。
Using the Code
随附的源代码解决方案包含五个项目
DataWebService | 通过 WCF 使用 LINQ 公开 Northwind 表。此服务还包含将 Silverlight 客户端传递的更改更新到数据库的逻辑。 |
UpdateChangesToLINQ | 使用 LINQ 数据上下文提供更新更改的逻辑。它由 _DataWebServices_ 项目使用。 |
TrackChanges | 跟踪对象更改。用于 _SilverlightUpdateChanges_。 |
DemoUpdateClass | 演示数据更新的控制台应用程序。有助于调试 _UpdateChanges_ 类。 |
SilverlightUpdateChanges | 一个基本的 Silverlight 应用程序,显示绑定到 Northwind _Customer_ 表的 _DataGrid_。通过调用 _DataWebService_ 来填充此表。 |
在运行示例之前,请在 _DataWebService_ 中更改路径,使其指向包含的 Northwind 数据库 MDF 文件。这是标准的 Northwind 示例数据库,并附加了一个 _testtable_ 表。此表包含大多数 SQL 数据类型,可用于测试各种类型,并且还可以显示 Silverlight 网格如何处理它们。
该解决方案有两个主要部分:跟踪 Silverlight 应用程序中进行的更改,以及提交更改以更新数据库。
跟踪更改
当将 Web 服务引用添加到 Silverlight 项目时,VS 2008 将生成代理类。这些类基于服务公开的类。因此,如果服务包含使用 _Customer_ 对象的对象,则会在 Silverlight 端创建相应的 Customer 代理对象。这些代理类是隐藏的,但您可以通过在“解决方案资源管理器”中选择“显示所有文件”按钮来查看它们。它们存储在 _Reference.cs_ 文件中,每个服务一个。
我在代理类中注意到的是 _INotifyPropertyChanged_ 接口中实现的数据结构。这要求实现 _PropertyChanged_ 事件处理程序,该处理程序在进行属性更改时被调用。默认情况下,此事件处理程序未分配。
以下代码包含一个实现 _INotifyPropertyChanged_ 事件的简单 _Widget_ 类
public partial class Widget: object, System.ComponentModel.INotifyPropertyChanged
{
private string _ID;
private string _Name;
private string _Colour;
public string ID
{
get
{
return this._ID;
}
set
{
if ((object.ReferenceEquals(this._ID, value) != true))
{
this._ID = value;
this.RaisePropertyChanged("ID");
}
}
}
public string Name
{
get
{
return this._Name;
}
set
{
if ((object.ReferenceEquals(this._Name, value) != true))
{
this._Name= value;
this.RaisePropertyChanged("Name");
}
}
}
public string Colour
{
get
{
return this._Colour;
}
set
{
if ((object.ReferenceEquals(this._Colour, value) != true))
{
this._Colour= value;
this.RaisePropertyChanged("Colour");
}
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler
propertyChanged = this.PropertyChanged;
if ((propertyChanged != null))
propertyChanged(this, new
System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
使用此事件处理程序的优点是,无论是通过代码还是数据控件进行更改,只要更改属性就会触发它。要连接事件处理程序,请将事件处理程序方法分配给对象。以下代码演示了如何将事件处理程序分配给 _Widget_ 对象。每次 _Widget_ 属性发生更改时,都会调用事件处理程序
public void TestHandler()
{
//
Widget testWidget = new Widget();
testWidget.ID = "100";
testWidget.PropertyChanged += new
System.ComponentModel.PropertyChangedEventHandler(this.Notifychanges);
testWidget.Name = "Big Widget";
testWidget.Colour = "Green";
}
public void Notifychanges(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
PropertyInfo p;
p = sender.GetType().GetProperty(e.PropertyName);
//get change property value
string changeValue = p.GetValue(sender, null).ToString();
System.Windows.Browser.HtmlPage.Window.Alert("Changing " +
e.PropertyName + " to " + changeValue);
}
随附的 _TrackChanges_ 类库通过使用此事件处理程序来跟踪对象更改。任何对象属性更改都会导致事件触发。更改存储在 _Dictionary_ 对象中。
要在项目中使用此功能,请添加对 _TrackChanges_ 对象的引用。 _TrackChanges_ 公开了 _ChangeTracking_ 类。类构造函数需要三个参数:表名、键数组以及对象集合或单个对象实例。
表名应与 Web 服务 LINQ 数据源中的 _TableName_ 属性匹配(有关更多信息,请参见下文)。键数组是区分大小写的,它们必须与对象属性名称的大小写匹配。
在以下示例中,将创建单个 _Customer_ 实例并将其分配给 _ChangeTracking_ 对象
Customer testCustomer = new Customer();
//need to assign an ID for tracking to know what to track..
testCustomer.CustomerID = "10000";
//start tracking changes
ChangeTracking customerTrack = new ChangeTracking("dbo.Customers",
new string[] { "CustomerID" }, testCustomer);
testCustomer.CompanyName = "Test Company";
testCustomer.ContactName = "Fred Smith";
testCustomer.ContactTitle = "Consultant";
_SilverlightUpdateChanges_ 示例项目将 Northwind _Customers_ 表和 _testtable_ 的所有记录加载到数据网格中。所做的任何更改将在单击“保存更改”按钮时提交到数据库。
以下代码列出了 _SilverlightUpdateChanges_ 项目的一部分。加载表单后,通过调用 Web 服务方法来填充客户和 testable 网格。这将返回这些表的所有记录列表。创建 _ChangeTrack_ 类的实例,传入表名、键数组和要跟踪更改的对象。创建此类会在每个对象上将 _NotifyChanges_ 事件处理程序连接到类内的事件处理程序。
TrackChanges.ChangeTracking customerTracking;
TrackChanges.ChangeTracking testTracking;
void Page_Loaded(object sender, RoutedEventArgs e)
{
//create an instance of the web service and get customers
NorthwindSvc.NorthwindSvcClient nwindClient =
new NorthwindSvc.NorthwindSvcClient();
//add event handler for GetCustomers asynchronous call
//and call GetCustomersAsync to populate items
nwindClient.GetCustomersCompleted += new
EventHandler<SilverlightUpdateChanges.NorthwindSvc.
GetCustomersCompletedEventArgs>(client_GetAllCustomersCompleted);
nwindClient.GetCustomersAsync();
nwindClient.GetTestItemsCompleted += new
EventHandler<GetTestItemsCompletedEventArgs>(
nwindClient_GetTestItemsCompleted);
nwindClient.GetTestItemsAsync();
}
void nwindClient_GetTestItemsCompleted(object sender,
GetTestItemsCompletedEventArgs e)
{
//get list of test items
List<testtable> testList = e.Result.ToList();
//start tracking any changes to testList
testTracking = new ChangeTracking("dbo.testtable",
new string[] { "id" }, testList);
grdTestItems.ItemsSource = testList;
grdTestItems.Columns[0].IsReadOnly = true;
}
void client_GetAllCustomersCompleted(object sender,
SilverlightUpdateChanges.NorthwindSvc.GetCustomersCompletedEventArgs e)
{
//get list of customers
List<Customer> customerList = e.Result.ToList();
//start tracking any changes to customerList
customerTracking = new ChangeTracking("dbo.Customers",
new string[] { "CustomerID" }, customerList);
grdCustomers.ItemsSource = customerList;
}
所有更改都存储在 _ChangeTracking_ 类中的 _Dictionary_ 对象中。字典键包含记录的键以及被更改的字段/属性。这些值由分隔符分隔。默认分隔符是竖线 ('|')。因此,如果 Northwind _Customers_ 表的 _CustomerName_ 属性对公司记录“Around the Horn”进行了更改,则键将如下所示:_AROUT|CustomerName_。已更改的值存储在相应的字典 _Value_ 属性中。
更新更改
可以通过调用 DataWebServices Web 服务 _SubmitChangesAsync_ 方法来将 _SilverlightUpdateChanges_ 的数据更改提交到数据库。此方法需要更改的字典对象和表名。
_DataWebServices_ 项目使用 _UpdateChanges_ 类来提交数据库更改。 _UpdateChanges_ 构造函数不需要参数。 _UpdateChanges_ 类公开了 _SubmitChanges_ 方法,该方法提交要进行的更改。 _SubmitChanges_ 需要您要更改的数据的 LINQ _DatabaseContext_ 对象、表名以及更改的字典对象。
表名参数必须与 LINQ 类中分配的表名属性匹配。您可以通过查看 LINQ DBML 文件中的类定义来查看属性。它看起来会像这样
[Table(Name="dbo.Customers")]
Public partial class Customer : INotifyPropertyChanging,
INotifyPropertyChanged
如果属性包含架构,则必须包含它。以下代码段调用 _SubmitChanges_ 方法,传递一个字典更改以更新 _dbo.Customers_ 表
NorthwindDataContext northwindDB = new NorthwindDataContext(connectionString);
//create a new UpdateChanges object and submit changes
//to the Customers database using the northwindDB LINQ Northiwind provider
UpdateChanges testChanges = new UpdateChanges();
return (testChanges.SubmitChanges(northwindDB,
"dbo.Customers", changesDictionary));
_SubmitChanges_ 方法将对每个更改的记录执行 _UPDATE_ 语句。它每个记录只执行一个 _UPDATE_ 语句。如果您对给定记录进行了多次更改,它将在单个语句中合并这些更新。
_SubmitChanges_ 返回一个字符串,显示成功更新的数量和执行的语句数量。
_UpdateChanges_ 类还公开了以下属性
属性 | 描述 |
ErrorList |
任何更新错误的异常列表。 |
SuccessCounter |
成功更新语句的数量。 |
ExecutionCounter |
执行更新语句的数量。 |
分隔符 |
键分隔符。默认为竖线 '|'。 |
问题
以当前形式使用这些类有一些优点和缺点。
优点
- 只发送更改
- 使用直接 _UPDATE_ 语句可实现快速数据库执行和最小的数据库流量
缺点
- 不提供乐观锁定机制
- 仅适用于 SQL Server
- 当前仅支持更新 - 不支持删除或添加
删除记录相对容易。插入也可能,但需要实现一种返回新值键的机制。添加乐观锁定需要将部分或全部字段传回。
在 Silverlight 端构建 SQL 字符串并将其发送回 Web 服务的选项已在我脑海中闪过。这将解决许多问题。但是,安全问题很大;能够针对数据库执行任意 SQL 语句太危险了。加密过程和安全标识方案可能可以解决其中一些问题。
另一个问题是键分隔符,目前设置为竖线 |。如果任何被更改的属性包含此值,更新操作将失败。可以通过将其更改为不太显眼的值来轻松修复此问题(跟踪类和更新类都可以通过 _Delimiter_ 属性进行更改)。如果属性名与相应的表字段名不同,则无法正常工作。可以通过提供映射例程轻松修复此问题。
我正在考虑一个映射例程,用于将查找值映射到字段名,这将加快操作速度并传输更少的数据。
怪癖
在处理 Silverlight 2 时,我遇到了一些问题。其中之一是默认启动项目。人们会认为这是 Silverlight 项目,但实际上是 Web 服务。如果将 Silverlight 项目设为启动项目,在调试模式下(按 F5)启动项目时可能会出现通信错误。但是,在非调试模式(Ctrl-F5)下运行可以正常工作。
另一个是偶尔出现的警告
Custom tool warning: Unable to load one or more of the requested types.
Retrieve the LoaderExceptions property for more information.
G:\Data\VS\VS2008\Silverlight\SilverlightUpdateChanges\SilverlightUpdateChanges\
Service References\NorthwindSvc\Reference.svcmap.
这似乎不影响执行,但很奇怪,并且可能与 Silverlight 的 Beta 状态有关。当 Visual Studio 重新启动时,它会消失。
结论
希望这能为从 Silverlight 更新数据库提供解决方案。我打算改进当前的功能,解决前面提到的一些问题。欢迎任何反馈,包括(建设性)批评。如果您打算评分低于 3,我将不胜感激。
历史
- 2008 年 8 月 31 日 - 第一个版本。