前文已经提到了,在IServerAgent的基础上,客户端也可以采用插件的结构形式,客户端插件需要实现IPassiveAddin接口。
我的想法是,当客户端主程序加载一个新的PassiveAddin时,可以在某个菜单的子Items上添加一项,当双击这个子菜单项时,则弹出该客户端插件提供的“业务操作窗体”。这只是使用客户端插件的可行方式之一,你完全可以根据你的应用来决定使用形式。IPassiveAddin接口定义如下:
“业务操作窗体”只有通过暴露的 Initialize 方法获取 IServerAgent 引用后,才能发送请求获取结果。 Initialize 方法的第二个参数说明当前时哪个用户在操作,这样客户端插件在构建请求消息时,需要将 userID 填充到请求消息的消息头中去,这样服务器才会知道这个消息的来源。
下面的代码说明了客户端主程序是如何加载IPassiveAddin的:
接着是 IPassiveAddin 实现:
我的想法是,当客户端主程序加载一个新的PassiveAddin时,可以在某个菜单的子Items上添加一项,当双击这个子菜单项时,则弹出该客户端插件提供的“业务操作窗体”。这只是使用客户端插件的可行方式之一,你完全可以根据你的应用来决定使用形式。IPassiveAddin接口定义如下:
1
///
<summary>
2 /// IPassiveAddin 用于客户端的插件。通常一个PassiveAddin对应着一个服务端的功能插件FunAddin
3 /// zhuweisky 2006.03.13
4 /// </summary>
5 public interface IPassiveAddin : IAddin
6 {
7 Type AddinFormType{ get ;} // AddinFormType必须实现IAddinForm接口
8 }
9
10 public interface IPassiveAddinForm
11 {
12 // PassiveAddin通过IServerAgent发送请求并获取结果
13 void Initialize(IServerAgent serverAgent , string userID) ;
14 }
IPassiveAddin
直接从
IAddin
继承,仅仅增加了一个属性
AddinFormType
,这个属性就是前面说的客户端插件提供的“业务操作窗体”。“业务操作窗体”必须从
IPassiveAddinForm
接口继承。2 /// IPassiveAddin 用于客户端的插件。通常一个PassiveAddin对应着一个服务端的功能插件FunAddin
3 /// zhuweisky 2006.03.13
4 /// </summary>
5 public interface IPassiveAddin : IAddin
6 {
7 Type AddinFormType{ get ;} // AddinFormType必须实现IAddinForm接口
8 }
9
10 public interface IPassiveAddinForm
11 {
12 // PassiveAddin通过IServerAgent发送请求并获取结果
13 void Initialize(IServerAgent serverAgent , string userID) ;
14 }
“业务操作窗体”只有通过暴露的 Initialize 方法获取 IServerAgent 引用后,才能发送请求获取结果。 Initialize 方法的第二个参数说明当前时哪个用户在操作,这样客户端插件在构建请求消息时,需要将 userID 填充到请求消息的消息头中去,这样服务器才会知道这个消息的来源。
下面的代码说明了客户端主程序是如何加载IPassiveAddin的:
1
private
void
LoadPassiveAddins()
2 {
3 this .lIToolStripMenuItem_addin.DropDownItems.Clear();
4
5 string directory = System.IO.Directory.GetParent(System.Windows.Forms.Application.ExecutablePath).FullName;
6 this .addinManagement.LoadAllAddins(directory, true );
7
8 foreach (IAddin addin in this .addinManagement.AddinList)
9 {
10 IPassiveAddin passiveAddin = addin as IPassiveAddin;
11 if (passiveAddin != null )
12 {
13 ToolStripItem item = new ToolStripMenuItem(passiveAddin.ServiceName, null , new EventHandler( this .OnAddinMenuClicked));
14 item.Tag = passiveAddin;
15 this .lIToolStripMenuItem_addin.DropDownItems.Add(item);
16 }
17 }
18 }
19
20 private void OnAddinMenuClicked( object sender, EventArgs e)
21 {
22 try
23 {
24 ToolStripItem item = (ToolStripItem)sender;
25 IPassiveAddin passiveAddin = (IPassiveAddin)item.Tag;
26 Form addinForm = (Form)Activator.CreateInstance(passiveAddin.AddinFormType);
27 ((IPassiveAddinForm)addinForm).Initialize( this .tcpServerAgent , this .currentUserID);
28 addinForm.Show();
29 }
30 catch (Exception ee)
31 {
32 ee = ee;
33 }
34 }
上述的介绍没有什么难点,仔细体会一下都能明白,就不多说了。这里我给出一个测试用的功能插件和对应的客户端插件示例。
示例的功能插件用于从
http://www.webservicex.net/globalweather.asmx 通过WebService
获取城市的天气信息,而客户端插件则用于为用户提供这项服务。
2 {
3 this .lIToolStripMenuItem_addin.DropDownItems.Clear();
4
5 string directory = System.IO.Directory.GetParent(System.Windows.Forms.Application.ExecutablePath).FullName;
6 this .addinManagement.LoadAllAddins(directory, true );
7
8 foreach (IAddin addin in this .addinManagement.AddinList)
9 {
10 IPassiveAddin passiveAddin = addin as IPassiveAddin;
11 if (passiveAddin != null )
12 {
13 ToolStripItem item = new ToolStripMenuItem(passiveAddin.ServiceName, null , new EventHandler( this .OnAddinMenuClicked));
14 item.Tag = passiveAddin;
15 this .lIToolStripMenuItem_addin.DropDownItems.Add(item);
16 }
17 }
18 }
19
20 private void OnAddinMenuClicked( object sender, EventArgs e)
21 {
22 try
23 {
24 ToolStripItem item = (ToolStripItem)sender;
25 IPassiveAddin passiveAddin = (IPassiveAddin)item.Tag;
26 Form addinForm = (Form)Activator.CreateInstance(passiveAddin.AddinFormType);
27 ((IPassiveAddinForm)addinForm).Initialize( this .tcpServerAgent , this .currentUserID);
28 addinForm.Show();
29 }
30 catch (Exception ee)
31 {
32 ee = ee;
33 }
34 }
先看服务端功能插件实现:
WeatherPreAddin
主要是
DealRequestMessage
方法
的实现,代码非常简单,通过WebService获取指定城市的天气情况,将返回的XML解析封装成IContract,然后返回给客户端。
接下来看客户端插件的实现,分为两步:首先是“业务操作窗体”界面设计。
该窗体要从IPassiveAddinForm接口继承。当点击按钮时,处理代码为:
button1_Click
private void button1_Click(object sender, System.EventArgs e)
{
string cityName = ESFramework.Common.ChsToSpellConverter.Convert(this.comboBox1.SelectedItem.ToString()) ;
WeatherReqContract body = new WeatherReqContract(this.contractHelper) ;
body.cityName = cityName ;
MessageHeader header = new MessageHeader(this.contractHelper) ;
header.TypeKey = int.Parse(this.textBox_asCityCode.Text.Trim()) ;
header.ServiceKey = 987 ;
header.UserID = this.curUserID ;
header.UserIDLen = this.contractHelper.GetBytesFromStr(this.curUserID).Length ;
header.MessageBodyLength = body.GetStreamLength() ;
ESFramework.Network.Message msg = new ESFramework.Network.Message(header ,body) ;
NetMessage resMsg = this.theAgent.CommitRequest(msg ,DataPriority.Common ,true) ;
if(resMsg.Header.Result != ServiceResultType.ServiceSucceed)
{
MessageBox.Show("没有发现对应的服务~!") ;
return ;
}
WeatherPredictionContract resContract = new WeatherPredictionContract(this.contractHelper) ;
resContract.FillMyself(resMsg.Body ,resMsg.BodyOffset) ;
this.groupBox1.Text = "服务结果--" + this.comboBox1.SelectedItem.ToString() ;
this.label_pressure.Text = resContract.Pressure ;
this.label_temp.Text = resContract.Temprature ;
this.label_vis.Text = resContract.Visbility ;
this.label_wind.Text = resContract.Wind ;
this.label_time.Text = resContract.PreTime ;
}
private void button1_Click(object sender, System.EventArgs e)
{
string cityName = ESFramework.Common.ChsToSpellConverter.Convert(this.comboBox1.SelectedItem.ToString()) ;
WeatherReqContract body = new WeatherReqContract(this.contractHelper) ;
body.cityName = cityName ;
MessageHeader header = new MessageHeader(this.contractHelper) ;
header.TypeKey = int.Parse(this.textBox_asCityCode.Text.Trim()) ;
header.ServiceKey = 987 ;
header.UserID = this.curUserID ;
header.UserIDLen = this.contractHelper.GetBytesFromStr(this.curUserID).Length ;
header.MessageBodyLength = body.GetStreamLength() ;
ESFramework.Network.Message msg = new ESFramework.Network.Message(header ,body) ;
NetMessage resMsg = this.theAgent.CommitRequest(msg ,DataPriority.Common ,true) ;
if(resMsg.Header.Result != ServiceResultType.ServiceSucceed)
{
MessageBox.Show("没有发现对应的服务~!") ;
return ;
}
WeatherPredictionContract resContract = new WeatherPredictionContract(this.contractHelper) ;
resContract.FillMyself(resMsg.Body ,resMsg.BodyOffset) ;
this.groupBox1.Text = "服务结果--" + this.comboBox1.SelectedItem.ToString() ;
this.label_pressure.Text = resContract.Pressure ;
this.label_temp.Text = resContract.Temprature ;
this.label_vis.Text = resContract.Visbility ;
this.label_wind.Text = resContract.Wind ;
this.label_time.Text = resContract.PreTime ;
}
注意,theAgent成员即是通过Initialize传入的IServerAgent引用!
接着是 IPassiveAddin 实现:
WeatherPassiveAddin
需要格外注意要实现AddinFormType属性,就是前面实现的“业务窗体”类型。
下图是功能服务器加载功能插件的截图:
下图是客户端加载客户插件后的截图:
下图是客户端插件提供服务的截图:
经过上述的介绍,读者应该对开发服务端的功能插件和客户端插件有些了解了。快结束的时候,再为下篇blog开个头。当我们开发了客户端插件和服务端插件后,做调试是一项非常麻烦的工作,因为不仅要启动应用服务器,还要启动客户端主程序、功能服务器才行。为了简化这个过程,我实现了一个Bridge应用程序,只需要加载一pair插件(服务端插件和对应的客户插件),即可进行两个插件的调试,而不用在启动客户端、AS、FS了。
感谢关注!
上一篇:ESFramework介绍之(7)-- 服务器代理IServerAgent
转到 :ESFramework 可复用的通信框架(序)