我们在开发WCF应用的过程中,经常碰见数据量太大无法传输的问题,有时可以通过在数据库中分页查取解决,但有时又必须一次性查询出所有数据发送到Client端,我们可以修改WCF配置,但这不是个根本的解决办法。例如,我们需要在客户端生成一个节点太多的树。经过研究发现可以采用多种方式解决这个问题,本文论述的是基于简单三层架构采用WCF的Duplex方式分页传输大量数据。
我们的Service要实现如下功能,可以将某次大批量的查询结果自动分页返回到客户端,可以在返回过程报告进度。
首先看看数据的获得,为了增加通用性,我们假设某查询需要某个类型的实例作为参数,返回的是某个类型的IList。这样可以为实际执行查询的类定义一个接口如下:
using System;
using System.Collections.Generic;
using System.Text;
namespace BNCommonPlus.ServiceHelper
...{
public interface IDALQueryDataListByPar<TResult,TPar>
...{
IList<TResult> GetDataByPar(TPar par);
}
}这是个范型接口,TResult代表返回结果类型,TPar代表查询参数类型。
那么我们的DAL层可以i这么写:
using System;
using System.Collections.Generic;
using System.Text;
//其他要引用的名字空间.....
namespace BNClinicService.DAL
...{
//假设我们需要获得一个BNV_RegisterInfo列表发送到客户端,该查询接收的是一个String作为参数
public class BNVClinicRegisterDAL : BNCommonPlus.ServiceHelper.IDALQueryDataListByPar<BNV_RegisterInfo, string>
...{
IDALQueryDataListByPar 成员#region IDALQueryDataListByPar<BNV_RegisterInfo,string> 成员
public IList<BNV_RegisterInfo> GetDataByPar(string QueryPar)
...{
IList<BNV_RegisterInfo> ListToReturn= new List<BNV_RegisterInfo>();
......//获取数据的代码
return ListToReturn;
}
}
}现在我们有的DAL,在BNVClinicRegisterDAL中GetDataByPar方法会得到大量数据需要分页发送到客户端。
显然,需要一个类自动将得到的数据分页,我们把它放在BLL层, 代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.ServiceModel;
using System.ServiceProcess;
namespace BNCommonPlus.ServiceHelper
...{
/** <summary>
/// 分页发送数据到客户端,对应BLL层
/// </summary>
/// <typeparam name="TResult">要发送的数据类型</typeparam>
/// <typeparam name="TPar">产生数据所需要的参数类型</typeparam>
public class DuplexDataTransferServiceBLLHelper<TResult,TPar>
...{
/** <summary>
/// 客户端标志
/// </summary>
private Guid _Guid;
/** <summary>
/// 要发送的数据
/// </summary>
private IList<TResult> ListToSendOut = new List<TResult>();
/** <summary>
/// 总行数
/// </summary>
private int RowCount = -1;
/** <summary>
/// 总页数
/// </summary>
private int PageCount = -1;
/** <summary>
/// 每页发送数据量
/// </summary>
private int PerPage = 30;
/** <summary>
/// 处理数据的DAL
/// </summary>
private IDALQueryDataListByPar<TResult, TPar> DALQueryDataListByPar = null;
private TPar QueryPar = default(TPar);
public DuplexDataTransferServiceBLLHelper(TPar par, Guid guid, IDALQueryDataListByPar<TResult, TPar> IDAL, int _PerPage)
...{
QueryPar = par;
_Guid = guid;
DALQueryDataListByPar = IDAL;
PerPage = _PerPage;
}
public DuplexDataTransferServiceBLLHelper(TPar par,Guid guid,IDALQueryDataListByPar<TResult,TPar> IDAL)
...{
QueryPar = par;
_Guid = guid;
DALQueryDataListByPar = IDAL;
}
public DuplexDataTransferServiceBLLHelper(TPar par, Guid guid)
...{
QueryPar = par;
_Guid = guid;
}
protected void SetIDAL(IDALQueryDataListByPar<TResult, TPar> IDAL)
...{
DALQueryDataListByPar = IDAL;
}
/** <summary>
/// 计算页数
/// </summary>
private void ComputePageCount()
...{
PageCount = RowCount / PerPage;
if (RowCount % PerPage != 0)
...{
PageCount += 1;
}
}
异步相关#region 异步相关
//异步相关
private delegate void HandlerDoQuery();
private void EndDoQuery(IAsyncResult result)
...{
HandlerDoQuery handler = result.AsyncState as HandlerDoQuery;
if (handler != null)
...{
handler.EndInvoke(result);
}
}
/** <summary>
/// 客户端开始查询
/// </summary>
/// <returns></returns>
public IAsyncResult BeginDoQuery()
...{
HandlerDoQuery handler = new HandlerDoQuery(DoQuery);
return handler.BeginInvoke(new AsyncCallback(EndDoQuery), handler);
}
#endregion
/** <summary>
/// 查询并发送
/// </summary>
private void DoQuery()
...{
try
...{
ListToSendOut = DALQueryDataListByPar.GetDataByPar(QueryPar);
RowCount = ListToSendOut.Count;
//送出总行数到客户端
FireEventSendListCountOut();
//计算页数
ComputePageCount();
for (int i = 0; i < PageCount; i++)
...{
List<TResult> list = new List<TResult>();
for (int t = i * PerPage; t < PerPage * (i + 1) && t < RowCount; t++)
...{
list.Add(ListToSendOut[t]);
}
//送出一页数据
FireEventSendListAPageOut(list,i);
}
//发送完成
FireEventSendAllListComplete();
}
catch(Exception ex)
...{
BNServiceEvents.FireEventServiceMethodExecError(ex);
}
}
/** <summary>
/// 送出查询结果列表行数
/// </summary>
/// <param name="ListCount">查询结果列表行数</param>
/// <param name="_guid"></param>
public delegate void HandlerSendListCountOut(int ListCount, Guid _guid);
/** <summary>
/// 送出查询结果列表行数
/// </summary>
public event HandlerSendListCountOut EventSendListCountOut;
private void FireEventSendListCountOut()
...{
HandlerSendListCountOut handler = EventSendListCountOut;
if (handler != null)
...{
handler(ListToSendOut.Count, _Guid);
}
}
/** <summary>
/// 送出查询一页结果列表
/// </summary>
/// <param name="_guid"></param>
/// <param name="list"></param>
public delegate void HandlerSendListAPageOut(Guid _guid, List<TResult> list,int PageNo);
/** <summary>
/// 送出查询一页结果列表
/// </summary>
public event HandlerSendListAPageOut EventSendListAPageOut;
private void FireEventSendListAPageOut(List<TResult> list,int PageNo)
...{
HandlerSendListAPageOut handler = EventSendListAPageOut;
if (handler != null)
...{
handler(_Guid, list, PageNo);
}
}
/** <summary>
/// 所有查询结果发送完毕
/// </summary>
/// <param name="_guid"></param>
public delegate void HandlerSendAllListComplete(Guid _guid);
/** <summary>
/// 所有查询结果发送完毕
/// </summary>
public event HandlerSendAllListComplete EventSendAllListComplete;
private void FireEventSendAllListComplete()
...{
HandlerSendAllListComplete handler = EventSendAllListComplete;
if (handler != null)
...{
handler(_Guid);
}
}
}
}这个类也是范型类,TResult代表返回结果类型,TPar代表查询参数类型。它默认按照每页30行分页数据。它包含三个事件
EventSendListCountOut 对应通知客户端一共有多少行数据需要发送。
EventSendListAPageOut 对应向客户端发送一页数据。
EventSendAllListComplete 对应通知客户端所有数据发送完成。
这个类支持异步查询发送。
在实际应用时我们可以这样用这个类:
using System;
using System.Collections.Generic;
using System.Text;
using BNCommonPlus.ServiceHelper;
using BNClinicService.Model;
using BNClinicService.DAL;
namespace BNClinicService.BLL
...{
/** <summary>
/// 获得Clinic挂号客户列表,Duplex分页返回数据
/// </summary>
public class GetBNVClientRegisterListBLL : DuplexDataTransferServiceBLLHelper<BNV_RegisterInfo,string>
...{
public GetBNVClientRegisterListBLL(string par, Guid _guid)
: base(par, _guid)
...{
base.SetIDAL(new BNVClinicRegisterDAL() as IDALQueryDataListByPar<BNV_RegisterInfo,string>);
}
}
}
那么Service的Callback接口可以定义如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
namespace BNCommonPlus.ServiceHelper
...{
public interface IBusinessDuplexServiceLibCallback<TResult>
...{
[OperationContract(IsOneWay = true)]
void SendQueryInfoByParRowCount(int RowCount);
[OperationContract(IsOneWay = true)]
void SendQueryInfoByParAPage(List<TResult> list, int PageNo);
[OperationContract(IsOneWay = true)]
void SendQueryInfoByParComplete();
}
}
这是个范型接口,TResult代表返回结果类型。
void SendQueryInfoByParRowCount(int RowCount);用于在查询完成后向客户端通报数据行数。
void SendQueryInfoByParAPage(List<TResult> list, int PageNo);用于在向客户端分页发送数据及页号。
void SendQueryInfoByParComplete();用于在通知客户端所有数据发送完成。
现在我们需要一个通用的Service来发送数据,代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.ServiceModel;
using System.ServiceProcess;
namespace BNCommonPlus.ServiceHelper
...{
public class DuplexDataTransferServiceLIBHelper<TResult,TPar>
...{
/** <summary>
/// 客户端回调字典
/// </summary>
protected Dictionary<Guid, IBusinessDuplexServiceLibCallback<TResult>> DICClients = new Dictionary<Guid, IBusinessDuplexServiceLibCallback<TResult>>();
/** <summary>
/// Server端查询BLL字典
/// </summary>
protected Dictionary<Guid, DuplexDataTransferServiceBLLHelper<TResult, TPar>> DICBLLs = new Dictionary<Guid, DuplexDataTransferServiceBLLHelper<TResult, TPar>>();
public void BeginDoSomeByPar( Guid _guid, DuplexDataTransferServiceBLLHelper<TResult, TPar> _DuplexDataTransferServiceBLLHelper)
...{
lock (DICClients)
...{
try
...{
//Client 加入Dic
DICClients.Add(_guid, Callback);
//查询类加入DIC
DICBLLs.Add(_guid, _DuplexDataTransferServiceBLLHelper);
//查询行数
_DuplexDataTransferServiceBLLHelper.EventSendListCountOut += new DuplexDataTransferServiceBLLHelper<TResult, TPar>.HandlerSendListCountOut(_DuplexDataTransferServiceBLLHelper_EventSendListCountOut);
/**/发送一页数据
_DuplexDataTransferServiceBLLHelper.EventSendListAPageOut += new DuplexDataTransferServiceBLLHelper<TResult, TPar>.HandlerSendListAPageOut(_DuplexDataTransferServiceBLLHelper_EventSendListAPageOut);
/**/所有数据发送完成
_DuplexDataTransferServiceBLLHelper.EventSendAllListComplete += new DuplexDataTransferServiceBLLHelper<TResult, TPar>.HandlerSendAllListComplete(_DuplexDataTransferServiceBLLHelper_EventSendAllListComplete);
//开始查询
_DuplexDataTransferServiceBLLHelper.BeginDoQuery();
}
catch (Exception ex)
...{
BNServiceEvents.FireEventServiceMethodExecError(ex);
}
}
}
/** <summary>
/// 完成某次查询结果的发送
/// </summary>
/// <param name="_guid"></param>
void _DuplexDataTransferServiceBLLHelper_EventSendAllListComplete(Guid _guid)
...{
lock (DICClients)
...{
IBusinessDuplexServiceLibCallback<TResult> _Callback;
if (DICClients.TryGetValue(_guid, out _Callback))
...{
try
...{
//通知客户端数据已经发送完成
_Callback.SendQueryInfoByParComplete();
//查询完成,结束相关类
DICClients.Remove(_guid);
DICBLLs.Remove(_guid);
DuplexDataTransferServiceEvents.FireEventServiceMethodCompleteOp(_guid);
}
catch (Exception ex)
...{
try
...{
DICClients.Remove(_guid);
}
catch
...{ }
try
...{
DICBLLs.Remove(_guid);
}
catch
...{ }
//BNServiceEvents.FireEventServiceMethodExecError(ex);//发生错误记录日志等操作
}
}
}
}
/** <summary>
/// 发送一页数据到客户端
/// </summary>
/// <param name="_guid"></param>
/// <param name="list"></param>
void _DuplexDataTransferServiceBLLHelper_EventSendListAPageOut(Guid _guid, List<TResult> list,int PageNo)
...{
lock (DICClients)
...{
IBusinessDuplexServiceLibCallback<TResult> _Callback;
if (DICClients.TryGetValue(_guid, out _Callback))
...{
try
...{
_Callback.SendQueryInfoByParAPage(list, PageNo);
DuplexDataTransferServiceEvents.FireEventServiceSendData(PageNo, list.Count, _guid);
}
catch (Exception ex)
...{
try
...{
DICClients.Remove(_guid);
}
catch
...{ }
try
...{
DICBLLs.Remove(_guid);
}
catch
...{ }
//BNServiceEvents.FireEventServiceMethodExecError(ex);//发生错误记录日志等操作
}
}
}
}
/** <summary>
/// 发送数据行数到客户端
/// </summary>
/// <param name="ListCount"></param>
/// <param name="_guid"></param>
void _DuplexDataTransferServiceBLLHelper_EventSendListCountOut(int ListCount, Guid _guid)
...{
lock (DICClients)
...{
IBusinessDuplexServiceLibCallback<TResult> _Callback;
if (DICClients.TryGetValue(_guid, out _Callback))
...{
try
...{
_Callback.SendQueryInfoByParRowCount(ListCount);
DuplexDataTransferServiceEvents.FireEventServiceSendDataCount(ListCount, _guid);
}
catch (Exception ex)
...{
try
...{
DICClients.Remove(_guid);
}
catch
...{ }
try
...{
DICBLLs.Remove(_guid);
}
catch
...{ }
//BNServiceEvents.FireEventServiceMethodExecError(ex);//发生错误记录日志等操作
}
}
}
}
IBusinessDuplexServiceLibCallback<TResult> Callback
...{
get
...{
return OperationContext.Current.GetCallbackChannel<IBusinessDuplexServiceLibCallback<TResult>>();
}
}
}
}我们可以使用这个Service了,在Server端我们这样写:
using System;
using System.Collections.Generic;
using System.Text;
using BNCommonPlus.ServiceHelper;
using BNClinicService.Model;
using BNClinicService.BLL;
namespace BNClinicService.ServiceLib
...{
public class BNClinicDataTransferServiceDuplexLib : BNClinicService.IServiceLib.IBNClinicDataTransferServiceDuplexLib, IBNDuplexDataTransferService
...{
IBNClinicServiceDuplexLib 成员#region IBNClinicServiceDuplexLib 成员
public void BeginDoQueryClinicRegisterByPar(string par, Guid _guid, BNCommonPlus.BNCommonModel.UserIdentity _UserIdentity)
...{
DuplexDataTransferServiceLIBHelper<BNV_RegisterInfo,string> _servicehelper=new DuplexDataTransferServiceLIBHelper<BNV_RegisterInfo,string>();
_servicehelper.BeginDoSomeByPar( _guid, new GetBNVClientRegisterListBLL(par, _guid));
DuplexDataTransferServiceEvents.FireEventServiceMethodBeforeOp("BNClinicService.ServiceLib.BNClinicDataTransferServiceDuplexLib", _UserIdentity.UserName, _guid);
}
#endregion
}
}这里需要说明的是IBNDuplexDataTransferService是个空接口,用来标识这个DuplexService,这个接口的目的将在以后描述。这个Service对应的Contract如下:
using System;
using System.Collections.Generic;
using System.Text;
using BNCommonPlus.ServiceHelper;
using BNClinicService.Model;
using System.ServiceModel;
namespace BNClinicService.IServiceLib
...{
[ServiceContract(Namespace = "http://BNClinicService.IBNClinicDataTransferServiceDuplexLib", SessionMode = SessionMode.Required,
CallbackContract = typeof(IBusinessDuplexServiceLibCallback<BNV_RegisterInfo>))]
public interface IBNClinicDataTransferServiceDuplexLib
...{
[OperationContract(IsOneWay = true)]
void BeginDoQueryClinicRegisterByPar(string par, Guid _guid, BNCommonPlus.BNCommonModel.UserIdentity UI);
}
}定义完成后再配置一下就可以使用这个Service了,新的问题来了,如果有很多类型的数据要传,我们需要为每一种数据类型,每一种查询方法建立不同的Service并配置它们,那样太痛苦。幸运的是目前找到了多种解决方法,具体解决方案将在以后描述。现在,我们看看用"SvcUtil.exe"生成的Client是怎样的
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceProcess;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
namespace BNClinicClient.BLL.ServiceClients
...{
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://BNClinicService.IBNClinicDataTransferServiceDuplexLib", ConfigurationName = "IBNClinicDataTransferServiceDuplexLib", CallbackContract = typeof(IBNClinicDataTransferServiceDuplexLibCallback), SessionMode = System.ServiceModel.SessionMode.Required)]
public interface IBNClinicDataTransferServiceDuplexLib
...{
[System.ServiceModel.OperationContractAttribute(IsOneWay = true, Action = "http://BNClinicService.IBNClinicDataTransferServiceDuplexLib/IBNClinicDataTransfe" +
"rServiceDuplexLib/BeginDoQueryClinicRegisterByPar")]
void BeginDoQueryClinicRegisterByPar(string par, System.Guid _guid, BNCommonPlus.BNCommonModel.UserIdentity _UserIdentity);
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IBNClinicDataTransferServiceDuplexLibCallback
...{
[System.ServiceModel.OperationContractAttribute(IsOneWay = true, Action = "http://BNClinicService.IBNClinicDataTransferServiceDuplexLib/IBNClinicDataTransfe" +
"rServiceDuplexLib/SendQueryInfoByParRowCount")]
void SendQueryInfoByParRowCount(int RowCount);
[System.ServiceModel.OperationContractAttribute(IsOneWay = true, Action = "http://BNClinicService.IBNClinicDataTransferServiceDuplexLib/IBNClinicDataTransfe" +
"rServiceDuplexLib/SendQueryInfoByParAPage")]
void SendQueryInfoByParAPage(List<BNClinicService.Model.BNV_RegisterInfo> list, int PageNo);
[System.ServiceModel.OperationContractAttribute(IsOneWay = true, Action = "http://BNClinicService.IBNClinicDataTransferServiceDuplexLib/IBNClinicDataTransfe" +
"rServiceDuplexLib/SendQueryInfoByParComplete")]
void SendQueryInfoByParComplete();
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IBNClinicDataTransferServiceDuplexLibChannel : IBNClinicDataTransferServiceDuplexLib, System.ServiceModel.IClientChannel
...{
}
//[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class BNClinicDataTransferServiceDuplexLibClient : System.ServiceModel.DuplexClientBase<IBNClinicDataTransferServiceDuplexLib>, IBNClinicDataTransferServiceDuplexLib
...{
public BNClinicDataTransferServiceDuplexLibClient(System.ServiceModel.InstanceContext callbackInstance)
:
base(callbackInstance)
...{
}
public BNClinicDataTransferServiceDuplexLibClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName)
:
base(callbackInstance, endpointConfigurationName)
...{
}
public BNClinicDataTransferServiceDuplexLibClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress)
:
base(callbackInstance, endpointConfigurationName, remoteAddress)
...{
}
public BNClinicDataTransferServiceDuplexLibClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress)
:
base(callbackInstance, endpointConfigurationName, remoteAddress)
...{
}
public BNClinicDataTransferServiceDuplexLibClient(System.ServiceModel.InstanceContext callbackInstance, System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress)
:
base(callbackInstance, binding, remoteAddress)
...{
}
public void BeginDoQueryClinicRegisterByPar(string par, System.Guid _guid, BNCommonPlus.BNCommonModel.UserIdentity _UserIdentity)
...{
base.Channel.BeginDoQueryClinicRegisterByPar(par, _guid, _UserIdentity);
}
}
}
注意:Callback接口发生了改变。
现在我们为客户端定义一个帮助类,代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.ServiceModel;
using System.ServiceProcess;
using System.Threading;
namespace BNCommonPlus.ClientHelper
...{
/** <summary>
///
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <typeparam name="TPar"></typeparam>
public abstract class DuplexDataTransferClientBLLHelper<TResult, TPar>
...{
/** <summary>
/// 客户端标志
/// </summary>
public Guid _Guid = Guid.NewGuid();
/** <summary>
/// 要发送的数据
/// </summary>
public List<TResult> ListToSendOut = new List<TResult>();
/** <summary>
/// 每页发送数据量
/// </summary>
public int PerPage = 30;
protected void ClearList()
...{
ListToSendOut = new List<TResult>();
}
public DuplexDataTransferClientBLLHelper(int _PerPage)
...{
PerPage = _PerPage;
}
public DuplexDataTransferClientBLLHelper(int _PerPage, Guid _guid)
:this(_PerPage)
...{
_Guid = _guid;
}
public DuplexDataTransferClientBLLHelper(Guid _guid)
...{
_Guid = _guid;
}
public DuplexDataTransferClientBLLHelper()
...{
}
/** <summary>
/// 客户端开始查询操作
/// </summary>
public abstract void StartDoQuery(TPar par);
/** <summary>
/// 客户端关闭Client
/// </summary>
public abstract void CloseClient();
/** <summary>
/// 服务器送回结果行数
/// </summary>
/// <param name="RowCount"></param>
protected void ReceiveQueryByParRowCount(int RowCount)
...{
FireEventReceiveQueryByParRowCount(RowCount);
}
/** <summary>
/// 服务器送回一页数据
/// </summary>
/// <param name="list">数据</param>
/// <param name="pageno">页号</param>
protected void ReceiveQueryByParAPage(List<TResult> list,int pageno)
...{
ListToSendOut.AddRange(list);
FireEventReceiveQueryByParAPage(list, pageno);
}
/** <summary>
/// 数据发送完毕
/// </summary>
protected void ReceiveQueryByParComplete()
...{
FireReceiveQueryByParCompleteWithData();
FireEventReceiveQueryByParComplete();
CloseClient();
}
/** <summary>
/// 服务器传回查询结果行数
/// </summary>
/// <param name="RowCount"></param>
public delegate void HandleReceiveQueryByParRowCount(int RowCount);
/** <summary>
/// 服务器传回查询结果行数事件
/// </summary>
public event HandleReceiveQueryByParRowCount EventReceiveQueryByParRowCount;
/** <summary>
/// 触发服务器传回查询结果行数事件
/// </summary>
/// <param name="RowCount"></param>
private void FireEventReceiveQueryByParRowCount(int RowCount)
...{
HandleReceiveQueryByParRowCount handler = EventReceiveQueryByParRowCount;
if (handler != null)
...{
handler(RowCount);
}
}
/** <summary>
/// 服务器传回一页数据
/// </summary>
/// <param name="list"></param>
public delegate void HandleReceiveQueryByParAPage(List<TResult> list,int PageNo);
/** <summary>
/// 服务器传回一页数据事件
/// </summary>
public event HandleReceiveQueryByParAPage EventReceiveQueryByParAPage;
/** <summary>
/// 触发服务器传回一页数据事件
/// </summary>
/// <param name="list"></param>
/// <param name="PageNo"></param>
private void FireEventReceiveQueryByParAPage(List<TResult> list,int PageNo)
...{
HandleReceiveQueryByParAPage handler = EventReceiveQueryByParAPage;
if (handler != null)
...{
handler(list,PageNo);
}
}
/** <summary>
/// 数据发送完成,携带数据
/// </summary>
public delegate void HandleReceiveQueryByParCompleteWithData(List<TResult> list);
/** <summary>
/// 数据发送完成,携带数据 事件
/// </summary>
public event HandleReceiveQueryByParCompleteWithData EventReceiveQueryByParCompleteWithData;
/** <summary>
/// 触发事件 数据发送完成,携带数据
/// </summary>
private void FireReceiveQueryByParCompleteWithData()
...{
HandleReceiveQueryByParCompleteWithData handler = EventReceiveQueryByParCompleteWithData;
if (handler != null)
...{
handler(ListToSendOut);
}
}
/** <summary>
/// 数据发送完成
/// </summary>
public delegate void HandleReceiveQueryByParComplete();
/** <summary>
/// 数据发送完成,通知客户类,此事件不携带数据
/// </summary>
public event HandleReceiveQueryByParComplete EventReceiveQueryByParComplete;
private void FireEventReceiveQueryByParComplete()
...{
HandleReceiveQueryByParComplete handler = EventReceiveQueryByParComplete;
if (handler != null)
...{
handler();
}
}
}
}
在客户端,我们这样使用这个类:
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using BNClinicService.Model;
using BNCommonPlus.ClientHelper;
using BNClinicClient.BLL.ServiceClients;
namespace BNClinicClient.BLL.ClinicRegister
...{
public class GetClinicRegisterInfo : DuplexDataTransferClientBLLHelper<BNV_RegisterInfo, string >, IBNClinicDataTransferServiceDuplexLibCallback
...{
private InstanceContext _InstanceContext = null;
private BNClinicDataTransferServiceDuplexLibClient client = null;
public override void StartDoQuery(string par)
...{
CloseClient();
ListToSendOut = new List<BNV_RegisterInfo>();
_InstanceContext = new InstanceContext(this);
client = new BNClinicDataTransferServiceDuplexLibClient(_InstanceContext, "NetTcpBinding_IBNClinicDataTransferServiceDuplexLib");
client.BeginDoQueryClinicRegisterByPar(par,_Guid,BNClinicClient.BLL.Common.ClientCache._UserIdentity);
}
public override void CloseClient()
...{
if (client != null)
...{
client.Close();
_InstanceContext = null;
}
}
IBNClinicDataTransferServiceDuplexLibCallback 成员#region IBNClinicDataTransferServiceDuplexLibCallback 成员
public void SendQueryInfoByParRowCount(int RowCount)
...{
base.ReceiveQueryByParRowCount(RowCount);
}
public void SendQueryInfoByParAPage(List<BNV_RegisterInfo> list, int PageNo)
...{
base.ReceiveQueryByParAPage(list, PageNo);
}
public void SendQueryInfoByParComplete()
...{
base.ReceiveQueryByParComplete();
}
#endregion
}
}
最后在UI层,可以这样写:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using BNClinicClient.BLL.ClinicRegister;
using BNClinicService.Model;
using BNCommonPlus.Controls;
namespace BNClinicClient.FormC.ClinicRegister
...{
public partial class FormClinicRegister : Form
...{
public FormClinicRegister()
...{
InitializeComponent();
}
private void FormClinicRegister_Load(object sender, EventArgs e)
...{
GetClinicRegisterInfo _GetClinicRegisterInfo = new GetClinicRegisterInfo();
_GetClinicRegisterInfo.EventReceiveQueryByParCompleteWithData += new BNCommonPlus.ClientHelper.DuplexDataTransferClientBLLHelper<BNV_RegisterInfo, string>.HandleReceiveQueryByParCompleteWithData(_GetClinicRegisterInfo_EventReceiveQueryByParCompleteWithData);
_GetClinicRegisterInfo.StartDoQuery("123456");
}
void _GetClinicRegisterInfo_EventReceiveQueryByParCompleteWithData(List<BNV_RegisterInfo> list)
...{
foreach (BNV_RegisterInfo model in list)
...{
treeView1.Nodes.Add(new MyTreeNode<BNV_RegisterInfo>(model,model.ClientID,true));
}
}
}
}本文描述了通过一个通用的DuplexService分页传输数据到客户端,还有很多其他解决方案将在以后描述
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lhzyn/archive/2008/04/01/2234496.aspx