显示自2008-08以来最新的 3 个帖子(共 7 个)。显示较早的帖子
显示自2008-08以来最新的 3 个帖子(共 7 个)。显示较早的帖子

2008年8月31日 星期日


最近,在群里讨论的时候看到一个很有趣的需求,所以研究了一下。下面是研究出来来的结果。

有没有想过,想用.NET做一个演示程序或者做个光盘的MENU。但却苦于,机器上没有.NET 而无法运行。

或许你认为这个很简单,做个安装程序不就可以了,但实际上,这样自动运行的程序,不适合制作安装程序, 或者说,他应该不需要安装。

也许你认为这个也容易,用Dot NET 虚拟机不久好了。没错,用这个,确实可以,不过会有几个问题。

第一,会有一个兼容性的问题。毕竟精简了.NET 类库,只有几个少数文件。
第二,版权问题。

实际上,大家可能都没有注意这个问题,但如果自己找,还是有人说的,飞信 用的那个虚拟机,是购买而来的。因此,你用了用了,就算是盗版了。这个问题,在国内,或许没人深究,但在国外就不同了,这也是我写本文的原因之一。

还有,另一种,解决方式,就是用VC++ 写一个引导,不过这对于我们这些不熟悉VC的人,是一个麻烦。

不知道,大家最近有没有关注 SQL2008呢?尝试过的人,一定会发现一个很细节的不同,那就是,安装的引导界面不同了。

SQL2008

实际上,在这个之前,还会有一个东西出来,那就是,提示你 安装 .NET Framework 3.5 SP1 。为什么会这样呢?因为,新的引导程序,就是.NET 做的了。

clip_image002

本文,将会指导你,如何让VS帮助我们生成这样的Loader,在没有安装.NET 的机器上,自动安装.NET Framework, 在已安装的机器上,直接运行程序。

注意: 由于需要手动修改项目文件, 因此操作错误,可能会使项目出现问题。请在对已有的工程修改前,做好备份。

先说一下原理, 做Client程序,应该都会知道ClickOnce,通过它,可以快速制作一个安装程序,来部署项目。在部署的时候,选择依赖,这样在安装的时候,如果客户端,没有相应的组件,就会自动安装。这个就是Bootstrapper,用于检查程序依赖项,并自动安装的启动程序。我们的任务,就是定这个Bootstrapper,将它指向的安装程序,对准我们实际要运行的程序,这样就达到了上面的目的。

那么我们开始吧,这里,为了演示,我将新建一个Win Form程序, 当然,你也可以用一个已有的项目,不过,记得要备份哦!


1、新建一个项目,名字为 AutorunDotNet。 这里要说明的是,我选择的是.NET 3.5,实际上,你也可以选择.NET 2.0 的程序。另外,要说的一点是,我用的是VB.NET ,但实际上,我们是修改的项目文件,和语言无关。

clip_image003
2、简单修改一下项目。 (实际上,可以不用做这个。)

clip_image001[12]

3、设置项目依赖。

a.双击解决方案中的“My Project” ,打开项目属性页。

clip_image005

b.选择 “发布” 选项卡, 单击 “系统必备”。

clip_image006

c.在“系统必备”里, 确保选择 “ 创建用于安装系统必备组件的安装程序”, 然后 下发,选择 需要安装的系统必备组件。 最后,在“指定系统必备组件的安装位置” 里,选择 “从与我的应用程序相同的位置下载系统必备组件”, 单击 确定 。(注:你可以不必设置这个,这样,安装程序,会在网络上下载,安装程序。)

clip_image007

恩,准备工作差不多了。下面开始修改项目文件吧。

4、编辑项目

a.卸载项目
右键单击项目, 选择 “卸载项目”
clip_image008
注:如果,VS没有显示, 需要打开 “工具” ——“选项”,找到“项目和解决方案”, 选择 “总是显示解决方案”。

clip_image009

b.编辑项目

右键 单击“AutotrunDotNet”,选择“编辑 AutorunDotNet.vbProj”。
clip_image010

5、在开的项目文件里,找到以下内容。(默认在项目结尾)
    clip_image011

a.将这段内容改成如下样子。

Code 
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
-->
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
<GenerateBootstrapper
ApplicationFile="$(TargetFileName)"
ApplicationName
="$(AssemblyName)"
ApplicationUrl
="$(_DeploymentFormattedApplicationUrl)"
BootstrapperItems
="@(BootstrapperPackage)"
OutputPath
="$(TargetDir)"
ComponentsLocation
="$(BootstrapperComponentsLocation)"
Path
="$(GenerateBootstrapperSdkPath)" />
</Target>

b.修改好后,保存。 在右键 单击“AutorunDotNet”项目,选择“重新加载项目”。

clip_image010[1]

6、OK,修改完成。编译一下看看。

如果没有错误,你将会在输出的文件夹下,看到一下内容。

clip_image012

这个Setup.exe,就是我们要的Loader。 2个文件夹里的是.NET 安装程序 和 Windows Installer 3.1 。

补充一点, 这个方法,目前还不能自动生成 Autorun.inf 。

因此只能我们自己手动写一个了,不是很难。( 虽然我很想人让它自动,生成,但没有找到相关资料。)

Autorun.inf

[autorun]
open
=setup.exe
icon
=setup.exe,0

其他说明

这个,我在VS2008 SP1上测试的, 根据MSDN的说法, 这个最低适用于 VS2005

如果怕影响当前项目,可以新建一个项目,专门用来做这个~

还有这里,简单说明一下 <GenerateBootstrapper > 就是用来生成实Bootstrapper 。

ApplicationFile="$(TargetFileName)" 这个指定要运行的程序名字,这里这个参数,是默认项目输出的名字。

ApplicationName="$(AssemblyName)" 程序的名字,即在执行的时候,提示的。这里是程序集的名字。

OutputPath="$(TargetDir)" 输出位置, 这里默认是项目输出的文件夹

其他的属性,请参考http://msdn.microsoft.com/zh-cn/library/ms164294.aspx

事例代码在这里下载 http://code.msdn.microsoft.com/AutorunDotNET


在这里简述一下,当初在群提出这个问题的情况。(由于实际信息,可能不方便说书,所以,决定,不说!!)

这个公司,给客户做了一个网站的演示程序,用WinForm作的。 之后,要把这个程序刻录到光盘,送给客户。

但因为是.NET 做的, 在没有.NET 的机器上,程序就无法运行。 反之,有因为商业程序,客户不见得会懂得很多开发信息。也不好让客户自己安装.NET 。

所以,需要一个确保的机制。 在没有.NET的机器上自动安装,之后并自动运行程序。

实际上,当时在讨论的时候,说了2点,一是 用 飞信的虚机, 二是写一个VC++的程序。

但这2点 都不符合, 一是,客户是 国外的,对用了带版权的东西,有多说头。 二是, 他们公司里面的没有人懂VC++。

=================

Q&A

=================

Q:错误 1 系统必备的安装位置未设置为“组件供应商的网站”,无法在磁盘上找到项“.NET Framework 3.5 SP1”中的文件“DotNetFX35SP1dotNetFX20aspnet.msp”。有关详细信息,请参见“帮助”。

A:VS 2008 SP1 升级后,并没安装.NET 3.5 SP1 完整版的 Bootstrapper,而只是复制了几个文件。 因此,默认是按配置中的信息,从网络上下载的在线安装版本。 你可以按照下面的方法,制作离线的完整版。或者在这里下载完整版的 复制到 Bootstrapper下。

首先, 去官方,下载 .NET 3.5 Sp1 完整版 2个中文语言包(x86 x64)

http://download.microsoft.com/download/2/0/e/20e90413-712f-438c-988e-fdaa79a8ac3d/dotnetfx35.exe

http://download.microsoft.com/download/7/7/b/77b96978-c805-4674-a9b0-43351e86b41f/dotnetfx35langpack_x64zh-CHS.exe

http://download.microsoft.com/download/7/7/b/77b96978-c805-4674-a9b0-43351e86b41f/dotnetfx35langpack_x86zh-CHS.exe

然后,找到Bootstrapper的本地位置。

这个位置,默认是Windows SDK安装的位置。

我这里的位置是 C:Program FilesMicrosoft SDKsWindowsv6.0ABootstrapper

C 盘是我的系统盘, V6.0A SDK的版本号, 如果你安装了其他版本, 有可能会使Bootstrapper 更改位置

在这个文件夹下,可以看到 DotNetFX35SP1 文件夹,把.NET 安装程序解压到这里, 如果怕操作失误,可以先备份一下。

1、用Winrar等工具,将dotnetfx35.exe 解压缩。

2、打开 解压缩的文件夹,进入到 wcudotNetFramework (用WinRAR查看的时候,有很多原因不明)

3、将dotNetFramework 下的所有内容,复制到(之前那个Bootstrapper里的) DotNetFX35SP1 下。

4、进入DotNetFX35SP1,下的zh-CHS 文件夹。

5、新建一个名为 DotNetFX35 文件夹,并进入。

6、新建2个文件夹,名字分别是 “x64”和 “x86”

7、将下载的x64语言包 dotnetfx35langpack_x64zh-CHS.exe 复制到 x64文件夹下

8、将下载的x86语言包 dotnetfx35langpack_x86zh-CHS.exe 复制到 x86”文件夹下

这样就完成了,你的VS应该可以正常的从本地复制 .NET 3.5 的完整安装。

完整版的下载链接在这里,虽然,很想弄成3个文件, 但没有成功。

http://www.mediafire.com/file/oe3kgyz1eis/DotnetFX35SP1.part1.rar
http://www.mediafire.com/file/fxfqb5lxtnd/DotnetFX35SP1.part2.rar
http://www.mediafire.com/file/pu405auktni/DotnetFX35SP1.part3.rar
http://www.mediafire.com/file/2hyammjnr5o/DotnetFX35SP1.part4.rar

将以上文件解压至 Bootstrapper 文件夹下即可。

如果链接出现问题,请回复。

PS.本打算,直接分享一个来的, 结果,看了一下容量, 我晕倒了。 316MB

如果能找到一个好的空间的话, 稍后会上传上来。

在这里说一个题外话,很多人都说.NET 太大了。 实际上,一点点细节没有注意。

.NET 3.5 之后,提供的程序包,里面,实际上包含了 X64 X86 2个平台的安装程序。另外,由于,Vista 以上的系统内置.NET ,因此,对这个平台的安装,又使用了MSU。所以,最终,的结果是,包含了2类系统的安装程序,每种又有X86X64平台的安装文件。

PS.刚才瞄了一下,MSU里,居然还有一个安腾的X64。那上面的就。。。

Q:怎样更改引导程序的图标和名称呢?
A:很抱歉,这个生成,图标资源,无法更改。因此,图标只能。。。 以后可能会研究看看,但希望恐怕不大, 因为 VS 所有生成的 安装程序(ClickOnce和MSI) 都是这个图标。但听说ClickOnce ,在.NET 3.5 SP1 有够个重大变革,可以自定义安装界面。不过这个还没有深入研究。如果,放入到光盘上的话,可以通过修改Autorun.inf 更改光盘的显示图标。
[autorun]
open=setup.exe
icon=AutorunDotNet.exe,0
将Icon指定光盘的图标,可以是个ICO文件。

至于,文件名称,这个可以自由更改,没有限制。


2008年8月29日 星期五

简介使用Sync Services for ADO.NET建立偶尔链接的程序

Introduction to Occasionally Connected Applications using Sync Services for ADO.NET


原文链接:http://msdn.microsoft.com/en-us/sync/bb887608.aspx








引言

Introduction

移动和远程工作能力在团队中的重要性与日俱增。最重要的是能让当团队中的成员在办公的时候,访问到相同的信息。在多数情况下,这些成员将有某种笔记本电脑,办公桌,智能手机或者PDA。通过这些设备,用户们可以快速访问他们的数据,通过VPN链接或其它方式连接到公司网络,像下面的样子。

The ability to support mobile and remote workers is becoming more and more important for organizations every day. It is critical that organizations ensure users have access to the same information they have when they are in the office. In most cases, these workers will have some sort of laptop, office desktop, Smartphone or PDA. From these devices, users may be able to access their data directly through VPN connections, Web servers or some other connectivity method into the corporate networks as seen below.



这种解决方案很容易实现。不幸的是,无法令外部员工满意。这类解决方案的主要缺点是:

This type of solution is fairly simple to implement. Unfortunately, for most remote workers it is less than satisfactory. Some major disadvantages of this type of solution include:



  1. 网络需求:为了能让用户访问他们的信息,远程设备在访问他们的数据时需要不间断的网络连接到过公司网络。对于一些像是在家里工作的员工,这可能不是问题。对于另一些员工,像是经常移动的销售代表,可能很困难。例如,如果一个销售代表访问一个客户,却因为缺乏网络而不能访问清单数据,这将让他很难有效率的完成他的工作。

  2. 数据访问速度: 在典型的C/S企业环境中,用户有着高速网络,是他们快速的获取信息。但是,外部员工普遍使用的是低速的,不可靠的有线或无线的网络。在这个解决方案中,每次用户请求的部分数据都需要被(重新)下载下来,因为没有方法在设备上保存数据。例如,如果一个销售代表在他每次打开程序的时候都需要下载他的产品类表,他将很快因为程序的长时间的等待而变得沮丧。

  3. 单点故障: 在这个解决方案中,所有的用户都将依赖于同一个服务器。如果由于计划的停机维护或者启动失败,而使数据库变得不可用,这将使所有外部员工将不能连接到他们的数据。

  4. 服务器的可扩展性:随着远程工作的员工变多,服务器的性能也受到影响,这将会产生添加额外硬件的需求。



  1. Network Requirements: In order to allow users to access their information, the remote device needs to have a constant connection to the corporate network while accessing their data. For some workers, such as those who are working from home, this may not be a problem. For others, such as sales reps who are constantly on the move, this may be more difficult. For example, if that sales rep were visiting a customer and was unable to access inventory data because of a lack of network connectivity, it would be very difficult for this user to effectively do their job.

  2. Data Access Speeds: In a typical client / server corporate environment, users have high speed networks that allow them quick access to information. Remote workers, however, are typically connected over slow, unreliable wired or wireless networks. With this solution, every piece of data this user needed would need to be downloaded every time it is requested because there is no way to persist the data on the device. For example, if a sales rep is required to download his product list every time he opens his application, he will quickly become frustrated with the time lag required to populate his application with information.

  3. Single Point of Failure: With this type of solution, all users are reliant on a single server. If that database becomes unavailable due to planned server downtime or from server failures, all of the remote workers will be disconnected from their data.

  4. Server Scalability: As more workers work remotely, the performance of the corporate servers will be affected, leading to a need to add additional hardware.



一种可以替代的解决方案是实现偶尔链接程序(Occasionally Connected Application (OCA))。OCA允许外部员工持续的访问他们的数据,但不像先前那样,用户直接访问企业数据库(情形),而是用户请求的信息储存在本地用户设备上。为了填充这个本地数据库,OCA程序通常都会包括一些数据同步能力。数据同步是由周期提取储存在客户端数据库(像SQL Server Compact)的信息并同步更改到服务端的数据库(像是SQL Server)。基于同步的解决方案的优势是用户不在需要长时间的持续连接来访问他们的数据。由于数据是保存在本地,它能提供持续的连接来访问他们的数据,将不需要中心数据库的处理。此外,也减小了网络限制和提高访问速度到达设备的速度。

One alternative to this solution is to implement an Occasionally Connected Application (OCA). An OCA allows a remote worker to continue to access their data, but unlike the previous scenario where the user accessed the corporate database directly, the information the worker requires is stored locally on the user’s device. In order to populate this user’s local database, an OCA will typically include some data synchronization capabilities. Data synchronization consists of the ability to periodically take information that is stored in the client database (such as SQL Server Compact) and synchronize changes with a server database (such as SQL Server). The advantage of a synchronization-based solution is that users are no longer required to have a constant network connection to access their information. Since their data is stored locally they are given constant access to their data while offloading processing requirements from the central database. Furthermore, the user is no longer limited by the network speed and can now access the data at the speed of the device.

在下面2个图表中我们见到OCA 的例子,数据(用绿色数据库表示)位于外部员工的本地设备上。第一个例子是直接储存到用户设备上的独立的数据库系统中。第二个例子是外部办公室将储存信息到工作组的数据库中,外部办公室的员工可以访问这个数据。

In the following two diagrams we can see examples of OCAs where data (represented by a green database) is persisted locally on the remote worker’s device. The first example is of a standalone database system where information is stored directly on the user’s device. The second example is of a remote office where information is stored in a workgroup database within this remote office so that multiple local workers can access the data.

OCA一种常见的扩展是支持多个数据库之间的数据协作。像下面的事例,一个远程的数据库自由的和其它数据库交换信息。这种解决方案当在一个小组成员在偏远位置工作并且不能访问中心数据库时很有用。这些员工经常需要向其他员工共享信息,但是他们不能连接到中央中数据库,他们需要共享信息通过一些像是P2P网络传输。

A common extension to this type of OCA is the ability to support data collaboration between databases. As seen below, a remote database is free to exchange information with any other database. This type of solution is useful when a team of people are working in remote locations and do not have access to a central database. These workers often need to share information amongst each other but since they do not have connectivity to the central database they need to share information through some sort of peer-to-peer network.

在本文剩下的部分里,我们将主要讨论OCASync Services for ADO.NET,这个关键的技术可以使你构建OCA程序。

Throughout the rest of this document we will discuss OCAs with a special emphasis on Sync Services for ADO.NET, a key technology that enables developers to build OCAs.

Sync Services for ADO.NET the Microsoft Sync Framework

Sync Services for ADO.NET and the Microsoft Sync Framework

Sync Services for ADO.NET 微软同步服务 (MSF)MSF是一个综合的同步平台,它可以使开发人员添加同步能力到应用程序,服务和设备中。MSF 解决了基本的问题,如何同步储存在任何位置中的任何数据类型使用任何协议在任何拓扑。MSF 的基本的能力是支持离线和任何类型终结点之间的数据协作(如:设备到桌面,桌面到服务器,等等)。

Sync Services for ADO.NET is a part of the Microsoft Sync Framework (MSF). MSF is a comprehensive synchronization platform that enables developers to add synchronization capabilities to applications, services and devices. MSF solves the fundamental problem of how to synchronize any type of data in any store using any protocol over any topology. Fundamental to MSF is the ability to support offline and collaboration of data between any types of endpoints (e.g. device to desktop, device to server, etc.).

Sync Services for ADO.NET 能够同步使用ADO.NET访问的数据库。由于Sync Services for ADO.NET MSF的一部分,任何使用uses Sync Services for ADO.NET 的数据库也能交换信息同其他的数据源,这些都是被MSF所支持的,像是Web 服务,文件系统,或者自定义数据储存。

Sync Services for ADO.NET enables synchronization between ADO.NET enabled databases. Since Sync Services for ADO.NET is part of the MSF, any database that uses Sync Services for ADO.NET can then also exchange information with other data sources that are supported by MSF, such as web services, file systems or custom data stores.

本文重点是在数据库系统中同步信息和Sync Services for ADO.NET 将如何帮助开发人员避免许多和OCA相关的常见问题。

The primary focus of this document will be on synchronizing information between database systems and how Sync Services for ADO.NET helps developers avoid many of the common issues associated with OCAs.

构建偶尔链接程序的挑战Challenges of Building an OCA

Challenges of Building an OCA

对于OCA,用户可以快速的访问他们数据并且不需要网络连接到中央数据中,以便获取他们的数据。OCA,先更新本地数据库,然后将数据更改同步到中心数据库上,而不是直接更新中心数据库。虽然 虽然 OCA解决直接访问中心数据库相关的关键问题,但在建立OCA时还有大量的挑战。下面几节将讨论这些挑战,并提出如何使用Sync Services for ADO.Net 来避免这些挑战的建议。

With an OCA, users have quick access to their data and do not require a network connection to the central database in order to access their information. With an OCA, updates are made locally and then synchronized into the central database, rather than being made directly at the central database,. Although OCAs solve the key problems associated with directly accesing a central server, there are a number of challenges associated with building OCAs. The following sections will discuss these challenges and propose ways that Sync Services for ADO.Net can be used to avoid these challenges.

修改跟踪

Change Tracking

为了使数据同步有效,某种修改跟踪的方法是必需的。修改跟中能提供数据库在某一时间点到另一个时间点之间的修改列表(插入、更新、删除)。想象一下,一个远程的用户链接到中央数据库并希望获取他从上次在线到现在的数据。没有修改跟踪,这个用户需要从中心数据源获取所有的数据然后向用户的计算机或设备上的本地数据库中合并数据的修改。在移动环境中,这是极其低效率的,因为使用无线网络交换这种信息将花费大量的时间。如果有大量的数据交换更会特别的慢。不幸的是,如果连接丢失数据则必须重新下载,这将会更加的没有效率。

In order to make data synchronization efficient, some method of change tracking is required. Change tracking is the ability to provide a list of changes (insert, updates and deletes) that were made to the database from one point in time to another. Imagine a remote user who connects to the central database and wishes to bring their data up-to-date since the last time they were online. Without change tracking, this user would need to take all of the data from the central data source and then merge that data with the changes that the user made to the local database on the user’s computer or device. In a mobile environment, this is extremely inefficient because of the amount of time a wireless network would take to exchange this information. It would be especially slow if large datasets were exchanged. Furthermore, if the connection drops the data must be re-downloaded, making this even less efficient.

为了避免这个问题,通常使用一个修改跟踪系统。一个比较流行的修改跟中方法是通过使用rowversions 和触发器(triggers)。这个方法需将rowversion列添加到每个表中。为了删除,可能需要一个独立表或者一个删除标记列通常需要记录这些被删除的行并通过触发器来维护。

To avoid these issues, a change-tracking system is typically used. One popular method for change tracking is through the use of rowversions and triggers. This method requires a rowversion column to be added to each table. For deletions, a separate table or a “deleted” flag column is typically required to log these removed rows that are maintained through triggers.

这种解决方案的主要缺点有:

  • 必须修改中心数据库的结构,添加列和表,这可能会影响到当前的程序。

  • 触发器将在每行被更改后出发,这可能会对性能产生影响。

  • 维护正确的rowversions和删除行的逻辑是很复杂的。

  • 长时间运行事务将可能会有数据在同步期间丢失,这回照成结果不一致。

The major disadvantages to this solution are:

  • Changes are required in the central database schema to add columns and tables that may affect current applications.

  • Triggers are fired for each change made to a row, which has performance implications.

  • Logic for maintaining proper rowversions and row deletions can get extremely complicated.

  • Long running transactions can result in some data being missed during synchronization, resulting in data inconsistencies.

对于SQL Server 2008,跟踪修改方法将有一个新选择SQL Server 2008 修改跟踪。修改追踪的原理是管理标记几个监视更改的表。之后, SQL Server 2008 将跟踪任何的插入、更新或删除。当远程 “requestor” 请求更改,SQL Server 2008 将会提供全部的更改,从“Requestor”指定的最后一次成功下载开始。 Sync Services for ADO.NET 使用SQL Server 2008 修改跟踪可以给OCA环境提供以下优势:

  • 不需要修改结构既可以跟踪修改

  • 触发器对于修改跟中不是必需的,这意味修改跟中将减少对服务器的影响。在某些情况下,使用发器跟踪的DML开销可能比SQL Server 2008 修改跟踪高400%之多。SQL Server 2008 修改跟踪开销少的原因是维护第二个索引。

  • 所有跟踪修改的逻辑都在SQL Server 引擎的内部,这样就减少了设置系统类型的复杂性。

  • 对于长时间事务的数据一致性问题将不再是问题。

  • 包括整合数据库管理特性,像是动态管理视图和安全。

SQL Server 2008 has introduced a new alternative method for tracking changes called SQL Server 2008 Change Tracking. The concept behind change tracking is that an administrator marks certain tables to be monitored for changes. From that point SQL Server 2008 keeps tracks of any inserts, updates or deletes that are made. When a remote “requestor” requests changes, SQL Server 2008 will provide all of the changes that have occurred since the last successful download as specified by the requestor. Sync Services for ADO.NET has been built to take advantage of SQL Server 2008 change tracking and provides the following advantages for an OCA environment:

  • No schema changes are required to be able to track changes

  • Triggers are not required for tracking changes, which means that tracking changes has far less of an impact on the server. In certain cases, the DML overhead associated with trigger based change tracking can be 400% greater than that of SQL Server 2008 change tracking. The overhead of enabling SQL Server 2008 change tracking is similar to the overhead of maintaining a second index.

  • All of the logic for tracking changes is internal to the SQL Server engine and as such reduces the complexity for setting up this type of system.

  • Data consistency issues associated with long running transactions are no longer an issue.

  • Includes integrated database administration feature such as Dynamic Management Views and Security

维护数据更改

Maintaining Change Data

修改跟踪表通常会快速增大,占用磁盘空间和影响查询效率。因此,合理的下一步是决定何时移除修改跟踪。根据用户最后一次同步来清理很困难,因为设备可能还没有同步或者用户只是简单的不使用它。这些因素使得管理员决定何时所有活动用户接受到更改并可以清理更改数据变的困难。

Change tracking tables will typically grow quite quickly, taking up storage space and affecting the performance of queries being executed against them. As such, the next logical step is to determine when tracked changes can be removed. Basing cleanup on when users have last synchronized is difficult because a device may have not been synchronized or the user may have simply stopped using it. These factors make it difficult for administrators to determine when all active users have received the changes which would allow them to clean up the change data.

SQL Server 2008中,可以定制阈值,用来维护更改数据和自动清理旧的数据。而且,这是运行时在后台进程,不会对服务器产生性能影响。

In SQL Server 2008, customizable retention thresholds are used to maintain this change data and automatically clean up old data. Furthermore, this process runs as a background process to help offset any performance impact on the server.

冲突检测和解决

Conflict Detection and Resolution

冲突是OCA可能出现的另一个问题。当2个或更多的对同一数据的数据库修改后,同步引擎将尝试将这些修改应用到一个数据库上,这时就发生了冲突。例如,想想一下,有2个销售代表尝试更新同一个客户的2个不同的地址。第一个销售代表成功同步更新了中心数据库。当下一个销售代表同步更新到主数据库中时,就会发生异常,因为当前行的状态和同步引擎期望的不同。有多种方式解决这个问题。例如,以最后一次更新为准或者根据用户的资历进行选择。

Conflicts are another issue that can arise in an OCA. Conflicts will occur when two or more databases make a change to the same piece of data and then the synchronization engine tries to apply those into a single database. For example, imagine two sales reps who try to update a customer’s address to two different values. The first sales rep successfully synchronizes the update to the central database. When the next sales rep goes to synchronize the update to the main database, a conflict occurs because the current state of the row is different from what the synchronization engine was expecting. There are a variety of ways to resolve these conflicts. For example, the last change to come in may be the one that wins, or alternatively it may be based on which user has the most seniority.

Sync Services for ADO.NET提供使用外部冲突检测和解决的能力配合SQL Server 2008 改进低复杂度的检测数据冲突的体验。使用这个技术,在上个例子中的第一个销售代表将成功更新中心服务器。新值将会被应用,因为至今没有冲突。当第二个销售代表上传更改时,一个冲突将被检测到,因为在中心服务器的变更版本(Change Version)不包含当前销售代表的数据库。之后,远程的程序逻辑将决定如何处理这个冲突。

Sync Services for ADO.NET provides conflict detection and resolution capabailities out-of-the-box and SQL Server 2008 improves on this experience by decreasing the complexity associated with identifiying conflicts. By using this technique, the first sales rep in our example will successfully upload the change to the central server. It will be applied since there is no conflict yet. When the second sales rep goes to upload the change, a conflict will be detected because the change version in the central server does not exist in the current sales rep database. From this point, logic in the remote application determines how to handle the conflict.

优先数据交换

Prioritizing Data Exchange

OCA成功部署后,通常需要一种方式来优化用户的数据交换一遍限制或者适应低速网络连接。一种优化数据同步的方式是定义数据的优先级,是高优先或者关键。使用优先级数据交换,关键更改将会立即被同步,次重要的数据将会稍后同步。例如,想想一下,当一个用户使用一个慢速网络访问,由于带宽有限,这个用户希望发送单个表的更改(高度重要),而其它表的更改将在之后连接高速网络后发送。对于Sync Services for ADO.NET,同步执行将从远端程序执行,基于表到表和只上传或只下载的同步。这意味着可以指定同步的表,其他的将在稍后同步,并且能够保证所有数据的交换。

When an OCA is deployed, users will often look for ways to optimize their data exchange given limited or slow network connections. One good way to optimize data synchronization is by prioritizing data to define data that is high priority or critical. Using priority data exchange, critical changes could be synchronized immediately while leaving less important data to be synchronized at a later time. For example, imagine a user currently has access only to a slow connection. Given the limited bandwidth, this user wishes to send change from a single (highly important) table and leave changes from other tables for later in the day when she can connect through a faster network connection. With Sync Services for ADO.NET, synchronization can be executed from the remote application on a table-by-table basis and on an upload-only or download-only basis. This means that specific tables can be synchronized, leaving others to be synchronized at a later time while still enabling guaranteed data exchange for all data.

后台同步

Background Synchronization

当用户的程序执行一个命令的时候(如,数据同步),也不会影响用户的控制。对于OCA,当执行诸如数据同步的时候,用户将不能访问本地数据库。

Nothing will frustrate a user more than when they are locked out of their application while another process (such as data synchronization) takes control. With most OCAs, users are not able to access their local database while processes like data synchronization are being executed.

对于Sync Services for ADO.NET,同步是在后台线程运行的。只要本地数据库支持在独立连接上同步(像 SQL Server Compact does),同步就可以在后台执行。这允许本地用户继续使用和修改数据库直到同步完成。

With Sync Services for ADO.NET, synchronization can run as a background thread. As long as the local database supports the ability to synchronize on a separate connection (as SQL Server Compact does), synchronization can be executed in the background. This allows a local user to continue to use and change their database while synchronization is being completed.

多同步拓扑

Multiple Synchronization Topologies

当构建OCA时,组织通常会选择一个网络扩谱。例如,想想一下,一组运送工,在一天开始从中心库存数据库中下载路线信息,然后在一天结束的时候,上传包囊和路线完成信息到同一库存数据库。在传统的同步环境中,这可能会被设计成放射状模型(hub-and-spoke),如下。

When first architecting an OCA, organizations will typically start with a single network topology. For example, imagine a group of delivery workers who download their route information at the start of the day from a central warehouse database and then upload their package and route completion information to the same warehouse database at the end of the day. In a traditional synchronization environment this would be considered a hub-and-spoke model as seen below.

然而,如果当一个员工完成了他的工作,却从仓库开到城市的另一端,这时司机可能希望同步数据到另一个仓库。或者进一步,使用数据协作将数据同步给另一个运送工。第二个用户回到仓库将他们数据都上传到服务器。你可以想象,这种同步逻辑将会很快变得很复杂。

However, what happens if a delivery worker completes his work on the other side of the city from the warehouse and the driver wishes to synchronize with a different warehouse? Or to take it one step further, why not let the delivery worker synchronize data with another delivery worker by using data collaboration. The second user could then go to a warehouse and upload both users’ data. As you can imagine the logic required to orchestrate this type of synchronization can quickly become very complicated.

对于Sync Services for ADO.NET,实际上支持使用任何类型同步拓扑。现在,公司将不再局限单个拓扑或者组合拓扑,这意味着可以建立包含离线和协作基础结构.

With Sync Services for ADO.NET, virtually any type of synchronization topology can be used. Now organizations are no longer limited to a single topology but can choose any one or a combination of topologies, meaning they could create combinations of offline and collaboration-based architectures.

自定义客户端和服务器数据库

Custom Client and Server Databases

毫无疑问,将来的某一时刻,你需要添加新的数据库到你的同步环境中。例如,一个企业可能拥有一个遗留系统或者大型主机需要整合到现有的同步环境中。或者,一个移动设备可能已经有了自定义数据库,需要将它整合到后端数据库。使问题更糟的是,如果数据库不允许你跟踪更改,使是你无法应用更改。Sync Services for ADO.NET,提供了通用数据库,像是SQL Server CompactSQL Server 2008 是外部功能。然而,使用Microsoft Sync Framework,开发人员可以闯进自定义提供器(provider),来整合同步存在任何位置的数据库,想Web serviceU 盘,有机上的SIM卡。

Without a doubt, there will come a time when you need to add a new database into your synchronization environment. For example, an enterprise may have legacy systems or mainframes that need to be integrated into the existing synchronization environment. Or alternatively, a mobile device may already have a custom database that needs to be integrated into the back-end database. To make matters worse, what if you are not able to make changes to that database to allow you to track changes? With Sync Services for ADO.NET, providers for common databases such as SQL Server Compact and SQL Server 2008 are included out of the box. However, using the Microsoft Sync Framework, a developer can create custom providers that integrate synchronization with virtually any place you store your data, whether that is a Web service, a USB key-chain drive, or a SIM card on a cell phone.

安全

Security

在一个OCA中,有多方面的安全需要实现。这些方面包括:

In an OCA, there are many aspects to security that may need to be implemented. Some of these areas include:

  • 数据库加密

  • 数据库认证

  • 加密同步数据

  • 内部认证

  • Database encryption

  • Database authentication

  • Encryption of data synchronization

  • Internal authentication

Sync Services for ADO.NET 能够帮助增加依赖同步的应用程序的安全性。在设备上, SQL Server Compact可以加密数据库,同时启动用户身份验证功能。从同步角度, Sync Services for ADO.NET 支持能使加密数据在2个数据库间传播。在企业端, SQL Server 2008,以及现有的IIS安全可以提供用户认证,认证用户交换数据。

Sync Services for ADO.NET helps to increase the security of applications that depend on synchronization. On the device side, SQL Server Compact offers the ability to both encrypt the database as well as the ability to enable user authentication. From a synchronization perspective, Sync Services for ADO.NET supports the ability to encrypt data as it travels between databases. On the corporate side, SQL Server 2008 as well as existing IIS security can be leveraged for user authentication as users exchange data.



总结

Summary

Sync Services for ADO.NET是一个中和数据同步解决方案,能使开发者构建支持任何数据库,任何数据库,在任何网络上的同步解决方案。对于Sync Services for ADO.NET Microsoft Sync Framework,信息同步的方向是可以进出组织的,允许开发者构建高效率和高伸缩性的偶尔连接程序。使用Microsoft Sync Framework开发者可以扩展Sync Services for ADO.NET 应用程序具备在设备间使用任何数据源协作的能力。

Sync Services for ADO.NET is a comprehensive data synchronization solution that enables developers to build solutions that support synchronization of any database, on any data protocol over any network topology. With Sync Services for ADO.NET and the Microsoft Sync Framework, the synchronization of information can flow virtually anywhere in or out of the organization, allowing developers to build efficient and highly scalable Occasionally Connected Applications. Using the Microsoft Sync Framework developers can extend Sync Services for ADO.NET applications to enable collaboration of data between devices using any data source.

关于Sync Services for ADO.NET Microsoft Sync Framework的更多信息,请访问: http://msdn2.microsoft.com/en-us/sync/default.aspx .

For more information about Sync Services for ADO.NET and the Microsoft Sync Framework, please visit: http://msdn2.microsoft.com/en-us/sync/default.aspx .


WCF使用自定义业务对象

Using Custom Business Objects with Windows Communication


原文链接:http://msdn.microsoft.com/zh-cn/vbasic/bb960413.aspx

作者:Rob Windsor Visual Basic MVP, ObjectSharp Consulting





在我之前的文章《Windows Communication Foundation 入门》中,我描述如何创建简单的WCF服务。服务事例演示了入户是使用简单类型,像数字和字符串,但在真实的应用程序中大多使用更复杂的数据类型,像是Customers Invoices。在这篇文章中,我将介绍如何在WCF中使用这些自定义的业务对象(类型)。

In my previous article, Getting Started with Windows Communication Foundation, I described the basics of creating and consuming a simple WCF service. The services demonstrated used simple types like numbers and strings, but most real-world applications use more complex data like customers and invoices. In this article I’ll demonstrate how to work with these custom business objects in WCF.

事例代码在这里下载。要运行事例代码,你需安装要Visual Studio 2005.NET Framework 3.0, Visual Studio 2005 extensions for .NET Framework 3.0 (WCF and WPF)(或者安装VS2008

Download the sample code here. To run the sample code, you'll need Visual Studio 2005, the .NET Framework 3.0, and the Visual Studio 2005 extensions for .NET Framework 3.0 (WCF and WPF) installed.

开始

Setting Up

我的演示的解决方案里包含4个项目。

I’ll start with a solution that contains four projects.

Business 是一个类库项目,包含自定义名称为Person的业务类。Person有几个属性(IdName,和BirthDate)和一个ToString 方法。

Business is a class library project with a custom business class named Person. Person has a few simple properties (Id, Name

, and BirthDate) and a ToString method.

Public Class Person


Private _id As Integer

Private _name As String

Private _birthDate As DateTime


Public Sub New(ByVal id As Integer, _

ByVal name As String, ByVal birthDate As DateTime)

_id = id

_name = name

_birthDate = birthDate

End Sub


Public Property Id() As Integer

Get

Return _id

End Get

Set(ByVal value As Integer)

_id = value

End Set

End Property


Public Property Name() As String

Get

Return _name

End Get

Set(ByVal value As String)

_name = value

End Set

End Property


Public Property BirthDate() As DateTime

Get

Return _birthDate

End Get

Set(ByVal value As DateTime)

_birthDate = value

End Set

End Property


Public Overrides Function ToString() As String

Return String.Format( _

"{0} [Id:{1}; Birth Date: {2}]", _

_name, _id, _birthDate)

End Function


End Class

Services 是一个类库项目,包含一个名为PersonService的服务类。它暴露有3个操作:获取单个Person对象,获取一组Person对象,和更新一个Person对象。因为服务使用内存存储数据(像字典),所以实例模式将设置成Single。这意味着一个服务对象将处理所有的过来的请求,并且这些请求将访问和修改字典中的数据。

Services is a class library project with a service class named PersonService. It exposes three operations: one to get a single Person object, one to get a group of Person objects, and one to update a Person object. Because the service uses an in-memory data store (i.e., the Dictionary), the instancing mode has been set to Single. This means that a single service object will handle all incoming requests, and those requests will access or modify the data inside the Dictionary.

Imports System.ServiceModel

Imports Business


<ServiceContract()> _

Public Interface IPersonService

<OperationContract()> Function GetPerson(ByVal id As Integer) As Person

<OperationContract()> Function GetPeople() As Person()

<OperationContract()> Sub UpdatePerson(ByVal p As Person)

End Interface


<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single)> _

Public Class PersonService

Implements IPersonService


Private _people As New Dictionary(Of Integer, Person)


Public Sub New()

_people.Add(1, New Person(1, "Frodo Baggins", #1/1/1380#))

_people.Add(2, New Person(2, "Sam Gamgee", #2/2/1385#))

_people.Add(3, New Person(3, "Merry Brandybuck", #3/3/1390#))

_people.Add(4, New Person(4, "Pippin Took", #4/4/1395#))

End Sub


Public Function GetPerson(ByVal id As Integer) As Person Implements IPersonService.GetPerson

Dim result As Person = Nothing

If _people.ContainsKey(id) Then

result = _people(id)

End If

Return result

End Function


Public Function GetPeople() As Person() Implements IPersonService.GetPeople

Dim result(_people.Count - 1) As Person

_people.Values.CopyTo(result, 0)

Return result

End Function


Public Sub UpdatePerson(ByVal p As Person) Implements IPersonService.UpdatePerson

If _people.ContainsKey(p.Id) Then

_people(p.Id) = p

End If

End Sub


End Class

Hosts 是一个控制台程序,它是我们服务的宿主。配置了一个使用了http://localhost:8081/PersonService地址和basicHttpBinding 的端点。

Hosts is a console application that will be our service host. An endpoint has been configured to use an address of “http://localhost:8081/PersonService” and the basicHttpBinding.

Imports System.ServiceModel


Module Module1


Sub Main()

Using host As New ServiceHost(GetType(Services.PersonService))

host.Open()


Console.WriteLine("Service started")

Console.ReadLine()

End Using

End Sub


End Module



<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<system.serviceModel>

<behaviors>

<serviceBehaviors>

<behavior name="MexEnabled">

<serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8081/PersonService" />

</behavior>

</serviceBehaviors>

</behaviors>

<services>

<service behaviorConfiguration="MexEnabled" name="Services.PersonService">

<endpoint

address="http://localhost:8081/PersonService"

binding="basicHttpBinding"

bindingConfiguration=""

contract="Services.IPersonService" />

</service>

</services>

</system.serviceModel>

</configuration>



最后, Client 是一Windows Forms 项目,将用来浏览也编辑数据。用户界面的元素已经添加到窗体,但还没有实现事件处理。

Finally, Client is a Windows Forms project that will be used to browse and edit data. The user interface elements have been added to the form, but none of the event handlers have been implemented.

数据契约

Data Contracts

一旦你准备好和编译后,右键单击Hosts项目,并选择调试->启动新的实例。你将收到一个像错误,它看起来像这样样子:

Once you have the solution set up and have a clean build, right-click the Hosts project and choose Debug > Start new instance. You should receive an error that looks like this:

不像ASMX Web ServicesWCF不允许自动序列化对象。所有你接收参数或方法的返回值得类型都必须是可序列化的。虽然你可以通过实现IXmlSerializable接口,或者应用Serializable attribute 到你的类上,来要完成这个,不过这里推荐使用新的DataContact DataMember attributes System.Runtime.Serialization 命名空间。

Unlike ASMX Web Services, WCF does not allow automatic serialization of objects. All types you receive as parameters or return from methods must be serializable. While this can be done by having your type implement the IXmlSerializable interface, or by applying the Serializable attribute to your class, the preferred method is to use the new DataContact and DataMember attributes from the System.Runtime.Serialization namespace.

为了解决InvalidDataContractException异常,我们需要添加这些attributes到我们自定义的业务对象上。右键单击Business项目并选择引用,选择System.Runtime.Serialization,然后单击确定。

To address the InvalidDataContractException, we will add these attributes to our custom business object. Right-click the Business project and select Add reference, select System.Runtime.Serialization, and click OK.

Person类的源代码文件的最顶端添加导入System.Runtime.Serialization命名空间的声明。然后将DataContract attribute添加到Person类的声明上和将DataMember attribute 添加到每个属性上。

Add an Imports statement for System.Runtime.Serialization to the top of the source code file for the Person class. Then add the DataContract attribute to the Person class declaration and the DataMember attribute to each of the properties.

Imports System.Runtime.Serialization


<DataContract()> _

Public Class Person


Private _id As Integer

Private _name As String

Private _birthDate As DateTime


Public Sub New(ByVal id As Integer, ByVal name As String, ByVal birthDate As DateTime)

_id = id

_name = name

_birthDate = birthDate

End Sub


<DataMember()> _

Public Property Id() As Integer

Get

Return _id

End Get

Set(ByVal value As Integer)

_id = value

End Set

End Property


<DataMember()> _

Public Property Name() As String

Get

Return _name

End Get

Set(ByVal value As String)

_name = value

End Set

End Property


<DataMember()> _

Public Property BirthDate() As DateTime

Get

Return _birthDate

End Get

Set(ByVal value As DateTime)

_birthDate = value

End Set

End Property


Public Overrides Function ToString() As String

Return String.Format("{0} [Id:{1}; Birth Date: {2}]", _name, _id, _birthDate)

End Function


End Class

经过这些修改,我们的服务应该可以使用了。右键单击Hosts项目,选择调试 ->启动新的实例。这次所有的东西应该正确的工作了:控制台窗口应该显示出来并且应该看到指示服务已经启动的消息。

With these changes, our service should be ready to go. Right-click the Hosts project and select Debug > Start new instance. This time everything should work correctly: the console window should appear and there should be an indication that the service has started.

让服务宿主继续运行,右键单击Client项目并选择添加服务引用。输入http://localhost:8081/PersonService到服务URI中并输入PersonServiceProxy 到服务引用名字中并点击确定。一旦将服务引用添加到客户端,就可以关闭服务宿主的控制台窗口了。

With the service host still running, right-click the Client project and select Add service reference. Enter “http://localhost:8081/PersonService” for the Service URI and PersonServiceProxy for the Service reference name, and then click OK. Once the service reference has been added to the client, close the service host console window.

代理类

Proxy Class

添加服务对话框做了2件事。一是添加客户端终结点到项目或网站的配置文件中。二是建立服务代理类。要查看代理类,选择Client项目并单击解决方案浏览器工具栏中的显示所有文件按钮。



The Add Service Reference dialog does two things. It adds a client endpoint to the configuration for the project or web site, and it creates a service proxy class. To see the proxy class, select the Client project and click the Show All Files button in the Solution Explorer’s toolbar.

双击PersonServiceProxy.vb 文件,在文本编辑器中打开。在这个部分,我们将说说Preson类。代理类不仅仅包含调用服务的代码,也包含客户端表示业务对象类。服务端发送Person数据将储存在这里。另外,请注意这个类仅用来表示数据,没有函数(方法)。原来类中的方法将不会包含在这里。

Double-click PersonServiceProxy.vb to open it in the text editor. The part we are interested in is the Person class. Not only does the proxy contain the code we need to call our service, it also has a client-side representation of our business class. When the data for a Person is sent from the service, it will be stored here. Also note that this class only represents the data, not the functionality. None of the methods from the original class will be included here.

<System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0"), _

System.Runtime.Serialization.DataContractAttribute([Namespace]:="http://schemas.datacontract.org/2004/07/Business"), _

System.SerializableAttribute()> _

Partial Public Class Person

Inherits Object

Implements System.Runtime.Serialization.IExtensibleDataObject

<System.NonSerializedAttribute()> _

Private extensionDataField As System.Runtime.Serialization.ExtensionDataObject

<System.Runtime.Serialization.OptionalFieldAttribute()> _

Private BirthDateField As Date

<System.Runtime.Serialization.OptionalFieldAttribute()> _

Private IdField As Integer

<System.Runtime.Serialization.OptionalFieldAttribute()> _

Private NameField As String

Public Property ExtensionData() As System.Runtime.Serialization.ExtensionDataObject Implements System.Runtime.Serialization.IExtensibleDataObject.ExtensionData

Get

Return Me.extensionDataField

End Get

Set

Me.extensionDataField = value

End Set

End Property

<System.Runtime.Serialization.DataMemberAttribute()> _

Public Property BirthDate() As Date

Get

Return Me.BirthDateField

End Get

Set

Me.BirthDateField = value

End Set

End Property

<System.Runtime.Serialization.DataMemberAttribute()> _

Public Property Id() As Integer

Get

Return Me.IdField

End Get

Set

Me.IdField = value

End Set

End Property

<System.Runtime.Serialization.DataMemberAttribute()> _

Public Property Name() As String

Get

Return Me.NameField

End Get

Set

Me.NameField = value

End Set

End Property

End Class

使用服务

Using the Service

现在我们有了代理类,我们可以向客户端添加逻辑了。我们将添加ListBox填充代码,在窗体载入的时候。为窗体的Load事件和ListBoxSelectedIndexChanged事件添加事件处理。Leave these empty for now.

Now that we have the proxy, we can add the logic to our client application. We will start by adding code to populate the list box when the form loads. Add event handlers for the form’s Load event and the list box's SelectedIndexChanged event. Leave these empty for now.

就如下面的代码,添加一个私有的子过程LoadListBox.在这个方法中我们建立一个服务实例并将调用GetPeople方法的返回值绑定到ListBox上。一旦ListBox填充了数据,我们将激活SelectedIndexChanged事件处理。为了执行这个方法,还需要在在窗体的Load事件中调用LoadListBox

Just below this code, add the declaration for a private subroutine called LoadListBox. In this method we want to create an instance of our service proxy and then data bind the results of calling GetPeople to the list box. Once the list box is populated, we want to activate the event handler for the SelectedIndexChanged event. With this method complete, have the form’s Load event call LoadListBox.

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

LoadListBox()

End Sub


Private Sub LoadListBox()

Using ws As New PersonServiceProxy.PersonServiceClient

PeopleListBox.DataSource = ws.GetPeople()

PeopleListBox.DisplayMember = "Name"

PeopleListBox.ValueMember = "Id"

End Using


PeopleListBox_SelectedIndexChanged(Me, EventArgs.Empty)

AddHandler PeopleListBox.SelectedIndexChanged, AddressOf PeopleListBox_SelectedIndexChanged

End Sub

现在,当用户选择ListBox中的项目时,我们希望显示关联的Person对象数据在下面的区域中。在SelectedIndexChanged 事件处理中,建立服务代理实例并调用GetPerson方法,将ListBoxSelectedValue 作为参数传入。我们需要一个变量来储存结果,所以我们声明了一个私有字段(如类级别的)叫做_person并使用它。然后将Person对象的属性值复制到文本框中。

Now, when the user selects an item in the list box, we want to show the data for the associated Person object in the data entry area below. In the SelectedIndexChanged event hander, create an instance of the service proxy and call GetPerson, passing in the SelectedValue from the list box (cast to an integer) as a parameter. We’ll need a variable to store the result, so declare a private field (i.e., a class-level variable) called _person and use it. Then copy the values of the properties from the Person object into the text boxes.

Private Sub PeopleListBox_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

Using ws As New PersonServiceProxy.PersonServiceClient

_person = ws.GetPerson(CInt(PeopleListBox.SelectedValue))

IdTextBox.Text = _person.Id

NameTextBox.Text = _person.Name

BirthDateTextBox.Text = _person.BirthDate.ToShortDateString()

End Using

End Sub

最后,在Save按钮的单击(Click)事件中,我们希望反转这个过程。将文本框里的值复制到_person 字段的属性中,建立服务代理实例,并调用UpdatePerson 发送更给到服务。最后,在调用LoadListBox方法来重新加载服务上的数据(你更改的)。

Finally, in the Save button’s Click event, we want to reverse this process. Copy the values of the text boxes into the _person field’s properties, create an instance of the service proxy, and then call UpdatePerson to send the changes back to the service. The last line should be a call to LoadListBox to reload the data (with your changes) from the service.

Private Sub SaveButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveButton.Click

_person.Id = IdTextBox.Text

_person.Name = NameTextBox.Text

_person.BirthDate = CDate(BirthDateTextBox.Text)

Using ws As New PersonServiceProxy.PersonServiceClient

ws.UpdatePerson(_person)

End Using

LoadListBox()

End Sub

完成后,你应该就能够测试了。我们需要启动Clientservice host 来运行程序,所以右键单击解决方案并选择属性。选择多启动项目并设置Hosts项目先于Client项目启动。

With this done, you should now be able to test. We are going to need both the client and the service host running to use the application, so right-click the solution and select Properties. Select Multiple startup projects and then set the Hosts project to start first followed by the Client project.

现在你应该能够浏览People列表,编辑数据并保存到服务。

You should now be able browse through the list of people (Hobbits actually), edit their data and save it back to the service.

只读属性

Read-Only Properties

你可能已经注意到了我们程序中的小问题,在测试的时候,我们可以编辑Person对象的ID属性。这可不是什么好事,由于我们的服务使用属性来识别Person对象。如果你编辑ID,并提交更改,结果可能被忽略或者更糟,你可能更改错误的Person对象的数据。

You may have noticed a small problem with our application while testing it: we can edit the Id property of the Person object. This is not a good idea, since we are using the property to identify the Person object in the service. If you edit the Id, your changes may be ignored or, worse yet, you may change the data for the wrong person.

要解决这个问题,定位到Person类并设置ID属性为只读。

To address this problem, go to the Person class and make the Id property read-only.

<DataMember()> _

Public ReadOnly Property Id() As Integer

Get

Return _id

End Get

End Property

清理解决方案,并右键单击Hosts项目,选择调试->启动新的实例。

Get a clean build on the solution, and then right-click the Hosts project and select Debug > Start new instance.

噢~~!它没有工作。就像ASMX Web Services,只有可以读写的实体才可以被序列话。有几种方式可以解决这个问题,我将描述其中的一种。

Arggghhh!! This won’t work. Just like ASMX Web Services, only entities that can be read from and written to can be serialized. There are several possible solutions for this issue. I will describe one of them.

解决方法

One Solution

自从设置只读属性后,将不能序列化,我们将更改Person类,让字段包含data contract 来替换原来的属性。开打Person类并移除DataMember attribute 3个属性中,并切添加它到3个字段声明上(如 _id_name,和_birthDate

Since read-only properties cannot be serialized, we are going to change the Person class so that the fields are included in the data contract instead of the properties. Open the Person class and remove the DataMember attribute from the three properties, and then add it to the declarations of the three fields (i.e. _id, _name, and _birthDate).

Imports System.Runtime.Serialization


<DataContract()> _

Public Class Person


<DataMember()> Private _id As Integer

<DataMember()> Private _name As String

<DataMember()> Private _birthDate As DateTime


Public Sub New(ByVal id As Integer, ByVal name As String, ByVal birthDate As DateTime)

_id = id

_name = name

_birthDate = birthDate

End Sub


Public ReadOnly Property Id() As Integer

Get

Return _id

End Get

End Property


Public Property Name() As String

Get

Return _name

End Get

Set(ByVal value As String)

_name = value

End Set

End Property


Public Property BirthDate() As DateTime

Get

Return _birthDate

End Get

Set(ByVal value As DateTime)

_birthDate = value

End Set

End Property


Public Overrides Function ToString() As String

Return String.Format("{0} [Id:{1}; Birth Date: {2}]", _name, _id, _birthDate)

End Function


End Class

既然我们更改了Person对象的序列化的样子,所以客户端的服务代理需要修改。你应该能够通过右键单击PersonServiceProxy.map 并选择更新服务引用来完成这个。不过我这建立是有问题的,代替的,右键单击服务引用文件夹,并选择删除。对App.config文件也做用羊的事。

Since we’ve changed how the Person object will look when it is serialized, the service proxy on the client needs to be modified. While you should be able to right-click PersonServiceProxy.map and select Update service reference to do this, I have found doing so problematic. Instead, right-click the Service References folder and select Delete. Do the same for the app.config file.

清理Hosts项目,并右键单击选择调试>启动新的实例。当服务Host运行的时候,右键单击并选择调试->穷调试。当服务宿主运行后,右键单击Client项目,并选择添加服务引用。在服务引用中输入服务地的URL http://localhost:8081/PersonService,并将PersonServiceProxy 添加到服务引用名著中,之后单击确定,一旦将服务引用添加到Client,就可以关闭服务宿主的控制台窗口了。

Get a clean build of the Hosts project, and then right-click and select Debug > Start new instance. With the service host running, right-click the client project and select Add service reference. Enter “http://localhost:8081/PersonService” for the Service URI and PersonServiceProxy for the Service reference name, and then click OK. Once the service reference has been added to the client, close the service host console window.

现在还没有解决我们的问题。客户端版本的Person_ID属性还是可读写的。我们需要使用真正的Person对象代替客户端代理生成的版本。右键单击Client项目并选择添加引用,然后选择项目选项卡,选择Business,单击确定。

This hasn’t really solved our problem. The _id property of the client-side version of Person is still read/write. What we need to do to address this issue is to use the real Person object on the client side instead of the version in the proxy. Right-click the Client project and select Add reference, then from the Projects tab, select Business, and click OK.

接下来将是冒险的部分,我们需要手动修改客户端代理类,即使在违背在代理类顶端注释中的警告。这个问题是因为,如果我们重新生成代理类,我们所作的所有修改将会丢失。所以,你需要记住在每次重新生成代理类的时候再次修改代码。值得欣慰的是,在VS2008添加服务引用对话框中将解决这个问题。

Here comes the dodgy part. We need to manually edit the client-side proxy—even though the comment at the top of the proxy warns you against doing so. This is a problem because, if we need to regenerate our proxy, all of the changes we are about to make will be blown away. So, you will need to remember to make the edits every time you regenerate the proxy. If it’s any comfort, the Add Service Reference dialog in Visual Studio 2008 will address this issue.

在代码编辑器中打开 PersonServiceProxy.vb 并移除Person类。然后替换所有出现的PersonServiceProxy.Person Business.Person。现在,在代码编辑器中打开Form1.vb并替换PersonServiceProxy.Person Business.Person。最后,在Save按钮单击事件中,移除设置ID属性的代码。由于ID不能更改,你也可以将显示文本框设置成只读。

Open PersonServiceProxy.vb in the code editor and remove the Person class from it. Then replace PersonServiceProxy.Person with Business.Person everywhere it appears. Now open Form1.vb in the code editor and replace PersonServiceProxy.Person with Business.Person everywhere it appears. Finally, in the Save button’s Click event hander, remove the line of code that sets the Id property. Since the Id cannot be changed, you may also want to make the text box that displays it read-only.

Imports System.ComponentModel


Public Class Form1


Private _person As Business.Person = Nothing


Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

LoadListBox()

End Sub


Private Sub LoadListBox()

Using ws As New PersonServiceProxy.PersonServiceClient

PeopleListBox.DataSource = ws.GetPeople()

PeopleListBox.DisplayMember = "Name"

PeopleListBox.ValueMember = "Id"

End Using


PeopleListBox_SelectedIndexChanged(Me, EventArgs.Empty)

AddHandler PeopleListBox.SelectedIndexChanged, AddressOf PeopleListBox_SelectedIndexChanged

End Sub


Private Sub PeopleListBox_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

Using ws As New PersonServiceProxy.PersonServiceClient

_person = ws.GetPerson(CInt(PeopleListBox.SelectedValue))

IdTextBox.Text = _person.Id

NameTextBox.Text = _person.Name

BirthDateTextBox.Text = _person.BirthDate.ToShortDateString()

End Using

End Sub


Private Sub SaveButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveButton.Click

_person.Name = NameTextBox.Text

_person.BirthDate = CDate(BirthDateTextBox.Text)

Using ws As New PersonServiceProxy.PersonServiceClient

ws.UpdatePerson(_person)

End Using

LoadListBox()

End Sub

End Class

你现在应该能够运行和测试完成的程序了。

You should now be able to run and test the completed application.

在客户端使用原Person类型的另一个好处是你可以使用它的方法。为了演示这个,我们将添加一些代码来调用Person类重写的ToString方法。添加按钮到客户端窗体上并双击他添加事件处理。事件处理的代码将PeopleListBoxSelectItem转换成Person对象并在消息框中显示调用ToString方法的结果。

Another benefit of using the real Person type on the client is that you can now make use of its methods. To demonstrate this, we will add some code to call the ToString method, which the Person class has overridden. Add the button to the client form and then double-click it to add an event handler. In the event handler add the code to cast the SelectItem from the PeopleListBox to a Person and then show the results of calling ToString in a message box.

Private Sub ToStringButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToStringButton.Click

Dim p As Business.Person


p = CType(PeopleListBox.SelectedItem, Business.Person)

MsgBox(p.ToString())

End Sub

当你单击按钮后,你看到的结果像如下样子:

When you click the button, you should see results that look like this:

销毁

Dispose()

有更多关于数据序列化的主题,在这个文章中只讲解了基本的和主要的问题。最常见的挑战是处理只读数据,我已经向你展示处理这种情况的技术。武装你的知识,你现在能够暴露和使用自定义业务对象的服务。

While there is more to the subject of data serialization, this article covers the fundamentals and the core issues. The most common challenge is dealing with read-only data, and I’ve shown you a technique to deal with this situation. Armed with this knowledge, you should now be able to build services that expose and consume custom business objects.