在WCF中分页传输数据(Duplex方式)

我们在开发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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值