在之前介绍的两个DEMO中,链接远程服务器所使用的方式都是通过数据库链接串。虽然可以简单
的实现所期望的数据同步功能,但这样做有两个问题:
1.不适合进行分布式布署.
2.安全方面存在问题,因为在客户端会持有远程数据库服务器的链接帐号。
因为这两个问题,导致了今天所要说的内容。当然在MSF中是支持采用WCF方式来进行远程数据访
问的,而且WCF不仅可以用于数据同步,还可以进行文件同步(会在后续章节中进行介绍)。
好了,费话少说,开始今天的正文吧。
下面简要介绍一下今天DEMO的一个具体开发流程。
首先我们采用我在
第二篇文章中所说的方式,用同步设计向导创建一个同步增量的MSF 对象,将
其名称命名为:
WcfSyncData.sync。
在生成相应的DataSet类(
WcfDataSet.xsd)文件之后,我们通过从“文件”-->"添加"-->"新建
项目"来构造一个WCF服务库,并将其命名为WcfSyncService。这样在我们的解决方案中就包含了两
个项目,一个是我们的同步DEMO,一个就是新建的WCF服务项目。
接下来我们在DEMO项目中的WcfSyncData.sync文件上双击,在弹出的“配置数据同步”窗口中
的“服务器项目位置”处的下拉列表中就可以看出我们刚才新建的 WCFSyncService项,我们选择它并
单击确定即可,如下图所示:
这样我们在看WcfSyncService项目中就会多出几个文件,它们是:
WcfSyncData.Server.Sync
WcfSyncData.Server.Designer.cs
WcfSyncData.Server.SyncContract.cs
其中的WcfSyncData.Server.Designer.cs内容就是以前在DEMO项目中的WcfSyncData.Designer.cs
中的关于服务端SyncAdapter(数据库访问操作,SQL命令等)部分的内容,这里设计器自动将这部分
代码迁移到了当前的WCF项目中,代码如下(相应的生成类内容分析参见
这篇文章):
Code
public partial class dnt_spacelinksSyncAdapter : Microsoft.Synchronization.Data.Server.SyncAdapter {
partial void OnInitialized();
public dnt_spacelinksSyncAdapter() {
this.InitializeCommands();
this.InitializeAdapterProperties();
this.OnInitialized();
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeCommands() {
// dnt_spacelinksSyncTableInsertCommand command.
this.InsertCommand = new System.Data.SqlClient.SqlCommand();
this.InsertCommand.CommandText = @" SET IDENTITY_INSERT dbo.dnt_spacelinks ON INSERT INTO dbo.dnt_spacelinks ([linkid], [userid], [linktitle], [linkurl], [description], [LastEditDate], [CreationDate]) VALUES (@linkid, @userid, @linktitle, @linkurl, @description, @LastEditDate, @CreationDate) SET @sync_row_count = @@rowcount SET IDENTITY_INSERT dbo.dnt_spacelinks OFF ";
this.InsertCommand.CommandType = System.Data.CommandType.Text;
this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@linkid", System.Data.SqlDbType.Int));
this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@userid", System.Data.SqlDbType.Int));
this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@linktitle", System.Data.SqlDbType.NVarChar));
this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@linkurl", System.Data.SqlDbType.VarChar));
this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@description", System.Data.SqlDbType.NVarChar));
this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@LastEditDate", System.Data.SqlDbType.DateTime));
this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@CreationDate", System.Data.SqlDbType.DateTime));
System.Data.SqlClient.SqlParameter insertcommand_sync_row_countParameter = new System.Data.SqlClient.SqlParameter("@sync_row_count", System.Data.SqlDbType.Int);
insertcommand_sync_row_countParameter.Direction = System.Data.ParameterDirection.Output;
this.InsertCommand.Parameters.Add(insertcommand_sync_row_countParameter);
// dnt_spacelinksSyncTableDeleteCommand command.
this.DeleteCommand = new System.Data.SqlClient.SqlCommand();
this.DeleteCommand.CommandText = "DELETE FROM dbo.dnt_spacelinks WHERE ([linkid] = @linkid) AND (@sync_force_write " +
"= 1 OR ([LastEditDate] <= @sync_last_received_anchor)) SET @sync_row_count = @@r" +
"owcount";
this.DeleteCommand.CommandType = System.Data.CommandType.Text;
this.DeleteCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@linkid", System.Data.SqlDbType.Int));
this.DeleteCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_force_write", System.Data.SqlDbType.Bit));
this.DeleteCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime));
System.Data.SqlClient.SqlParameter deletecommand_sync_row_countParameter = new System.Data.SqlClient.SqlParameter("@sync_row_count", System.Data.SqlDbType.Int);
deletecommand_sync_row_countParameter.Direction = System.Data.ParameterDirection.Output;
this.DeleteCommand.Parameters.Add(deletecommand_sync_row_countParameter);
// dnt_spacelinksSyncTableUpdateCommand command.
this.UpdateCommand = new System.Data.SqlClient.SqlCommand();
this.UpdateCommand.CommandText = @"UPDATE dbo.dnt_spacelinks SET [userid] = @userid, [linktitle] = @linktitle, [linkurl] = @linkurl, [description] = @description, [LastEditDate] = @LastEditDate, [CreationDate] = @CreationDate WHERE ([linkid] = @linkid) AND (@sync_force_write = 1 OR ([LastEditDate] <= @sync_last_received_anchor)) SET @sync_row_count = @@rowcount";
this.UpdateCommand.CommandType = System.Data.CommandType.Text;
this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@userid", System.Data.SqlDbType.Int));
this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@linktitle", System.Data.SqlDbType.NVarChar));
this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@linkurl", System.Data.SqlDbType.VarChar));
this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@description", System.Data.SqlDbType.NVarChar));
this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@LastEditDate", System.Data.SqlDbType.DateTime));
this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@CreationDate", System.Data.SqlDbType.DateTime));
this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@linkid", System.Data.SqlDbType.Int));
this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_force_write", System.Data.SqlDbType.Bit));
this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime));
System.Data.SqlClient.SqlParameter updatecommand_sync_row_countParameter = new System.Data.SqlClient.SqlParameter("@sync_row_count", System.Data.SqlDbType.Int);
updatecommand_sync_row_countParameter.Direction = System.Data.ParameterDirection.Output;
this.UpdateCommand.Parameters.Add(updatecommand_sync_row_countParameter);
// dnt_spacelinksSyncTableSelectConflictDeletedRowsCommand command.
this.SelectConflictDeletedRowsCommand = new System.Data.SqlClient.SqlCommand();
this.SelectConflictDeletedRowsCommand.CommandText = "SELECT [linkid], [DeletionDate] FROM dbo.dnt_spacelinks_Tombstone WHERE ([linkid]" +
" = @linkid)";
this.SelectConflictDeletedRowsCommand.CommandType = System.Data.CommandType.Text;
this.SelectConflictDeletedRowsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@linkid", System.Data.SqlDbType.Int));
// dnt_spacelinksSyncTableSelectConflictUpdatedRowsCommand command.
this.SelectConflictUpdatedRowsCommand = new System.Data.SqlClient.SqlCommand();
this.SelectConflictUpdatedRowsCommand.CommandText = "SELECT [linkid], [userid], [linktitle], [linkurl], [description], [LastEditDate]," +
" [CreationDate] FROM dbo.dnt_spacelinks WHERE ([linkid] = @linkid)";
this.SelectConflictUpdatedRowsCommand.CommandType = System.Data.CommandType.Text;
this.SelectConflictUpdatedRowsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@linkid", System.Data.SqlDbType.Int));
// dnt_spacelinksSyncTableSelectIncrementalInsertsCommand command.
this.SelectIncrementalInsertsCommand = new System.Data.SqlClient.SqlCommand();
this.SelectIncrementalInsertsCommand.CommandText = "SELECT [linkid], [userid], [linktitle], [linkurl], [description], [LastEditDate]," +
" [CreationDate] FROM dbo.dnt_spacelinks WHERE ([CreationDate] > @sync_last_recei" +
"ved_anchor AND [CreationDate] <= @sync_new_received_anchor)";
this.SelectIncrementalInsertsCommand.CommandType = System.Data.CommandType.Text;
this.SelectIncrementalInsertsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime));
this.SelectIncrementalInsertsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_new_received_anchor", System.Data.SqlDbType.DateTime));
// dnt_spacelinksSyncTableSelectIncrementalDeletesCommand command.
this.SelectIncrementalDeletesCommand = new System.Data.SqlClient.SqlCommand();
this.SelectIncrementalDeletesCommand.CommandText = "SELECT [linkid], [DeletionDate] FROM dbo.dnt_spacelinks_Tombstone WHERE (@sync_in" +
"itialized = 1 AND [DeletionDate] > @sync_last_received_anchor AND [DeletionDate]" +
" <= @sync_new_received_anchor)";
this.SelectIncrementalDeletesCommand.CommandType = System.Data.CommandType.Text;
this.SelectIncrementalDeletesCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_initialized", System.Data.SqlDbType.Bit));
this.SelectIncrementalDeletesCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime));
this.SelectIncrementalDeletesCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_new_received_anchor", System.Data.SqlDbType.DateTime));
// dnt_spacelinksSyncTableSelectIncrementalUpdatesCommand command.
this.SelectIncrementalUpdatesCommand = new System.Data.SqlClient.SqlCommand();
this.SelectIncrementalUpdatesCommand.CommandText = @"SELECT [linkid], [userid], [linktitle], [linkurl], [description], [LastEditDate], [CreationDate] FROM dbo.dnt_spacelinks WHERE ([LastEditDate] > @sync_last_received_anchor AND [LastEditDate] <= @sync_new_received_anchor AND [CreationDate] <= @sync_last_received_anchor)";
this.SelectIncrementalUpdatesCommand.CommandType = System.Data.CommandType.Text;
this.SelectIncrementalUpdatesCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime));
this.SelectIncrementalUpdatesCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_new_received_anchor", System.Data.SqlDbType.DateTime));
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeAdapterProperties() {
this.TableName = "dnt_spacelinks";
}
}
public partial class WcfSyncDataServerSyncProvider : Microsoft.Synchronization.Data.Server.DbServerSyncProvider {
private dnt_spacelinksSyncAdapter _dnt_spacelinksSyncAdapter;
partial void OnInitialized();
public WcfSyncDataServerSyncProvider() {
string connectionString = global::WcfSyncService.Properties.Settings.Default.ServertestConnectionString;
this.InitializeConnection(connectionString);
this.InitializeSyncAdapters();
this.InitializeNewAnchorCommand();
this.OnInitialized();
}
public WcfSyncDataServerSyncProvider(string connectionString) {
this.InitializeConnection(connectionString);
this.InitializeSyncAdapters();
this.InitializeNewAnchorCommand();
this.OnInitialized();
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public dnt_spacelinksSyncAdapter dnt_spacelinksSyncAdapter {
get {
return this._dnt_spacelinksSyncAdapter;
}
set {
this._dnt_spacelinksSyncAdapter = value;
}
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeConnection(string connectionString) {
this.Connection = new System.Data.SqlClient.SqlConnection(connectionString);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeSyncAdapters() {
// Create SyncAdapters.
this._dnt_spacelinksSyncAdapter = new dnt_spacelinksSyncAdapter();
this.SyncAdapters.Add(this._dnt_spacelinksSyncAdapter);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeNewAnchorCommand() {
// selectNewAnchorCmd command.
this.SelectNewAnchorCommand = new System.Data.SqlClient.SqlCommand();
this.SelectNewAnchorCommand.CommandText = "Select @sync_new_received_anchor = GETUTCDATE()";
this.SelectNewAnchorCommand.CommandType = System.Data.CommandType.Text;
System.Data.SqlClient.SqlParameter selectnewanchorcommand_sync_new_received_anchorParameter = new System.Data.SqlClient.SqlParameter("@sync_new_received_anchor", System.Data.SqlDbType.DateTime);
selectnewanchorcommand_sync_new_received_anchorParameter.Direction = System.Data.ParameterDirection.Output;
this.SelectNewAnchorCommand.Parameters.Add(selectnewanchorcommand_sync_new_received_anchorParameter);
}
}
除此之外,设计器向导还为我们自动生成了WcfSyncData.Server.SyncContract.cs文件,这个就是
相应的WCF服务类文件,里面的内容定义如下:
public
partial
class
WcfSyncDataSyncService :
object
, IWcfSyncDataSyncContract {
private
WcfSyncDataServerSyncProvider _serverSyncProvider;
public
WcfSyncDataSyncService() {
this
._serverSyncProvider
=
new
WcfSyncDataServerSyncProvider();
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public virtual SyncContext ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet,
SyncSession syncSession) {
return
this
._serverSyncProvider.ApplyChanges(groupMetadata, dataSet, syncSession);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public
virtual
SyncContext GetChanges(SyncGroupMetadata groupMetadata, SyncSession syncSession) {
return
this
._serverSyncProvider.GetChanges(groupMetadata, syncSession);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public
virtual
SyncSchema GetSchema(Collection
<
string
>
tableNames, SyncSession syncSession) {
return
this
._serverSyncProvider.GetSchema(tableNames, syncSession);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public
virtual
SyncServerInfo GetServerInfo(SyncSession syncSession) {
return
this
._serverSyncProvider.GetServerInfo(syncSession);
}
}
[ServiceContractAttribute()]
public
interface
IWcfSyncDataSyncContract {
[OperationContract()]
SyncContext ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet,
SyncSession syncSession);
[OperationContract()]
SyncContext GetChanges(SyncGroupMetadata groupMetadata, SyncSession syncSession);
[OperationContract()]
SyncSchema GetSchema(Collection
<
string
>
tableNames, SyncSession syncSession);
[OperationContract()]
SyncServerInfo GetServerInfo(SyncSession syncSession);
}
按照该文件上方提供的关于app.config配置结点信息,我们将相应的服务配置信息定义如下,首先是
services 节中:
<
service
name
="WcfSyncService.WcfSyncDataSyncService"
behaviorConfiguration
="WcfSyncService.WcfSyncDataSyncServiceBehavior"
>
<
host
>
<
baseAddresses
>
<
add
baseAddress
="http://localhost:8731/Design_Time_Addresses/WcfSyncService/WcfSyncDataSyncService/"
/>
</
baseAddresses
>
</
host
>
<
endpoint
address
=""
binding
="wsHttpBinding"
contract
="WcfSyncService.IWcfSyncDataSyncContract"
>
<
identity
>
<
dns
value
="localhost"
/>
</
identity
>
</
endpoint
>
<
endpoint
address
="mex"
binding
="mexHttpBinding"
contract
="IMetadataExchange"
/>
</
service
>
然后是
serviceBehaviors 节中:
<
behavior
name
="WcfSyncService.WcfSyncDataSyncServiceBehavior"
>
<
serviceMetadata
httpGetEnabled
="True"
/>
<
serviceDebug
includeExceptionDetailInFaults
="False"
/>
</
behavior
>
这样配置完成后,我们将当前WCF作为启动项目并编译运行,然后将相应的服务地址链接信息拷贝出来,
然后在DEMO项目中以“添加服务引用”的方式添加进来。
注:本DEMO的链接地址为:
http://localhost:8731/Design_Time_Addresses/WcfSyncService/WcfSyncDataSyncService/
接下来,我们只要在创建同步代理(SyncAgent)时创建相应的WCF服务(client)类实例,并将其绑
定到代理类的 RemoteProvider 属性上即可,相应代码如下(位于WcfSyncForm.cs文件中):
WcfSyncDataSyncAgent syncAgent
=
new
WcfSyncDataSyncAgent(
new
MSF_WinFormDemo.WcfServiceSync.WcfSyncDataSyncContractClient());
syncAgent.dnt_spacelinks.SyncDirection
=
Microsoft.Synchronization.Data.SyncDirection.Bidirectional;
syncAgent.Synchronize();
//
将本地数据同步到远程数据服务器
大家注意看上面的new MSF_WinFormDemo.WcfServiceSync.WcfSyncDataSyncContractClient()
即是我们在添加Web服务引用时生成的服务代理类。通过调用WcfSyncDataSyncAgent构造重载方法将代
理类实例绑定到syncAgent实例的RemoteProvider上,相应的重载构造方法如下所示(位于DEMO项目中
的WcfSyncData.Designer.cs文件):
public
WcfSyncDataSyncAgent(
object
remoteSyncProviderProxy) {
this
.InitializeSyncProviders();
this
.InitializeSyncTables();
this
.RemoteProvider
=
new
Microsoft.Synchronization.Data.ServerSyncProviderProxy(remoteSyncProviderProxy);
this
.OnInitialized();
}
而接下来的其余代码就与之前我写过的那两个DEMO中的代码没有什么区别了。看来采用WCF方式进行远程
数据访问很容易,主要就是将服务引用到本地,并进行同步代理RemoteProvider属性的相关绑定即可。
到今天为止,有关于MSF中
Sync Service for ADO.NET的内容就要先告一段落了。从下一篇文章开始,我
会介绍在MSF中如何进行
文件同步(File Sync),对这方面感兴趣的朋友敬请留意。
好了,今天的内容就先到这里了:)
原文链接:
http://www.cnblogs.com/daizhj/archive/2008/11/20/1337245.html
作者: daizhj, 代震军
Tags: 微软同步框架,ado.net,sqlce,wcf
网址: http://daizhj.cnblogs.com/
DEMO下载,请
点击这里:)