ESFramework介绍之(8)-- 客户端插件IPassiveAddin

前文已经提到了,在IServerAgent的基础上,客户端也可以采用插件的结构形式,客户端插件需要实现IPassiveAddin接口。
我的想法是,当客户端主程序加载一个新的PassiveAddin时,可以在某个菜单的子Items上添加一项,当双击这个子菜单项时,则弹出该客户端插件提供的“业务操作窗体”。这只是使用客户端插件的可行方式之一,你完全可以根据你的应用来决定使用形式。IPassiveAddin接口定义如下:

1 /// <summary>
2 /// IPassiveAddin用于客户端的插件。通常一个PassiveAddin对应着一个服务端的功能插件FunAddin
3 /// zhuweisky2006.03.13
4 /// </summary>
5 public interface IPassiveAddin:IAddin
6 {
7 TypeAddinFormType{ get ;} // AddinFormType必须实现IAddinForm接口
8 }
9
10 public interface IPassiveAddinForm
11 {
12 // PassiveAddin通过IServerAgent发送请求并获取结果
13 void Initialize(IServerAgentserverAgent, string userID);
14 }

IPassiveAddin 直接从 IAddin 继承,仅仅增加了一个属性 AddinFormType ,这个属性就是前面说的客户端插件提供的“业务操作窗体”。“业务操作窗体”必须从 IPassiveAddinForm 接口继承。
“业务操作窗体”只有通过暴露的 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 (IAddinaddin in this .addinManagement.AddinList)
9 {
10 IPassiveAddinpassiveAddin = addin as IPassiveAddin;
11 if (passiveAddin != null )
12 {
13 ToolStripItemitem = 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,EventArgse)
21 {
22 try
23 {
24 ToolStripItemitem = (ToolStripItem)sender;
25 IPassiveAddinpassiveAddin = (IPassiveAddin)item.Tag;
26 FormaddinForm = (Form)Activator.CreateInstance(passiveAddin.AddinFormType);
27 ((IPassiveAddinForm)addinForm).Initialize( this .tcpServerAgent, this .currentUserID);
28 addinForm.Show();
29 }
30 catch (Exceptionee)
31 {
32 ee = ee;
33 }
34 }

上述的介绍没有什么难点,仔细体会一下都能明白,就不多说了。这里我给出一个测试用的功能插件和对应的客户端插件示例。 示例的功能插件用于从 http://www.webservicex.net/globalweather.asmx 通过WebService 获取城市的天气信息,而客户端插件则用于为用户提供这项服务。

先看服务端功能插件实现:

ContractedBlock.gif ExpandedBlockStart.gif WeatherPreAddin
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->1publicclassWeatherPreAddin:IFunAddin
2{
3privateIContractHelpercontractHelper=newContractHelper();
4
5publicWeatherPreAddin()
6{
7
8}
9
10#regionIAddin成员
11
12publicstringServiceName
13{
14get
15{
16//TODO:添加WeatherPreAddin.ServiceNamegetter实现
17return"天气预测服务";
18}
19}
20
21publicvoidOnLoading()
22{
23//TODO:添加WeatherPreAddin.OnLoading实现
24}
25
26publicstringCatalogName
27{
28get
29{
30//TODO:添加WeatherPreAddin.CatalogNamegetter实现
31return"";
32}
33}
34
35publicintServiceKey
36{
37get
38{
39//TODO:添加WeatherPreAddin.ServiceKeygetter实现
40return987;
41}
42}
43
44publicstringDescription
45{
46get
47{
48//TODO:添加WeatherPreAddin.Descriptiongetter实现
49return"";
50}
51}
52
53publicvoidBeforeTerminating()
54{
55//TODO:添加WeatherPreAddin.BeforeTerminating实现
56}
57
58publicboolEnabled
59{
60get
61{
62//TODO:添加WeatherPreAddin.Enabledgetter实现
63returntrue;
64}
65set
66{
67//TODO:添加WeatherPreAddin.Enabledsetter实现
68}
69}
70
71publicfloatVersion
72{
73get
74{
75//TODO:添加WeatherPreAddin.Versiongetter实现
76return1;
77}
78}
79
80publicstringAddinType
81{
82get
83{
84//TODO:添加WeatherPreAddin.AddinTypegetter实现
85returnnull;
86}
87}
88
89publicAddinAppendixInfoAddinAppendixInfo
90{
91get
92{
93//TODO:添加WeatherPreAddin.AddinAppendixInfogetter实现
94returnnull;
95}
96}
97
98#endregion
99
100#regionIDataDealer成员
101
102publicESFramework.Network.NetMessageDealRequestMessage(ESFramework.Network.NetMessagereqMsg)
103{
104
105try
106{
107stringurl="http://www.webservicex.net/globalweather.asmx";
108string[]args=newstring[2];
109args[0]=this.contractHelper.GetStrFromStream(reqMsg.Body,reqMsg.BodyOffset,reqMsg.Header.MessageBodyLength);
110args[1]="China";
111objectresult=WebServiceHelper.InvokeWebService(url,"GetWeather",args);
112
113XmlParserparser=newXmlParser(result.ToString(),null);
114WeatherPredictionContractbody=newWeatherPredictionContract(this.contractHelper);
115body.Pressure=parser.GetSingleLayerConfigValue("Pressure");
116body.PressureLen=this.contractHelper.GetBytesFromStr(body.Pressure).Length;
117
118body.PreTime=parser.GetSingleLayerConfigValue("Time");
119body.PreTimeLen=this.contractHelper.GetBytesFromStr(body.PreTime).Length;
120
121body.Temprature=parser.GetSingleLayerConfigValue("Temperature");
122body.TempratureLen=this.contractHelper.GetBytesFromStr(body.Temprature).Length;
123
124body.Visbility=parser.GetSingleLayerConfigValue("Visibility");
125body.VisbilityLen=this.contractHelper.GetBytesFromStr(body.Visbility).Length;
126
127body.Wind=parser.GetSingleLayerConfigValue("Wind");
128body.WindLen=this.contractHelper.GetBytesFromStr(body.Wind).Length;
129
130reqMsg.Header.MessageBodyLength=body.GetStreamLength();
131
132returnnewNetMessage(reqMsg.Header,body.ToStream(),0);
133
134}
135catch(Exceptionee)
136{
137ee=ee;
138reqMsg.Header.MessageBodyLength=0;
139reqMsg.Header.Result=ServiceResultType.FailureByOtherCause;
140returnnewNetMessage(reqMsg.Header,null);
141}
142}
143
144#endregion
145}

主要是 DealRequestMessage 方法 的实现,代码非常简单,通过WebService获取指定城市的天气情况,将返回的XML解析封装成IContract,然后返回给客户端。

接下来看客户端插件的实现,分为两步:首先是“业务操作窗体”界面设计。
passiveForm.JPG

该窗体要从IPassiveAddinForm接口继承。当点击按钮时,处理代码为:

ContractedBlock.gif ExpandedBlockStart.gif button1_Click
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--> privatevoidbutton1_Click(objectsender,System.EventArgse)
{
stringcityName=ESFramework.Common.ChsToSpellConverter.Convert(this.comboBox1.SelectedItem.ToString());
WeatherReqContractbody=newWeatherReqContract(this.contractHelper);
body.cityName=cityName;
MessageHeaderheader=newMessageHeader(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.Messagemsg=newESFramework.Network.Message(header,body);
NetMessageresMsg=this.theAgent.CommitRequest(msg,DataPriority.Common,true);
if(resMsg.Header.Result!=ServiceResultType.ServiceSucceed)
{
MessageBox.Show("没有发现对应的服务~!");
return;
}

WeatherPredictionContractresContract=newWeatherPredictionContract(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 实现:
ContractedBlock.gif ExpandedBlockStart.gif WeatherPassiveAddin
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->1 publicclassWeatherPassiveAddin:IPassiveAddin
2{
3publicWeatherPassiveAddin()
4{
5}
6
7#regionIPassiveAddin成员
8
9publicTypeAddinFormType
10{
11get
12{
13returntypeof(AddinForm);//AddinForm即前面实现的业务窗体
14}
15}
16
17#endregion
18
19#regionIAddin成员
20
21publicstringServiceName
22{
23get
24{
25//TODO:添加WeatherPassiveAddin.ServiceNamegetter实现
26return"天气预测服务";
27}
28}
29
30publicvoidOnLoading()
31{
32//TODO:添加WeatherPassiveAddin.OnLoading实现
33}
34
35publicstringCatalogName
36{
37get
38{
39//TODO:添加WeatherPassiveAddin.CatalogNamegetter实现
40returnnull;
41}
42}
43
44publicintServiceKey
45{
46get
47{
48//TODO:添加WeatherPassiveAddin.ServiceKeygetter实现
49return987;
50}
51}
52
53publicstringDescription
54{
55get
56{
57//TODO:添加WeatherPassiveAddin.Descriptiongetter实现
58returnnull;
59}
60}
61
62publicvoidBeforeTerminating()
63{
64//TODO:添加WeatherPassiveAddin.BeforeTerminating实现
65}
66
67publicboolEnabled
68{
69get
70{
71//TODO:添加WeatherPassiveAddin.Enabledgetter实现
72returntrue;
73}
74set
75{
76//TODO:添加WeatherPassiveAddin.Enabledsetter实现
77}
78}
79
80publicfloatVersion
81{
82get
83{
84//TODO:添加WeatherPassiveAddin.Versiongetter实现
85return1;
86}
87}
88
89publicstringAddinType
90{
91get
92{
93//TODO:添加WeatherPassiveAddin.AddinTypegetter实现
94returnnull;
95}
96}
97
98publicAddinAppendixInfoAddinAppendixInfo
99{
100get
101{
102//TODO:添加WeatherPassiveAddin.AddinAppendixInfogetter实现
103returnnull;
104}
105}
106
107#endregion
108}

需要格外注意要实现AddinFormType属性,就是前面实现的“业务窗体”类型。

下图是功能服务器加载功能插件的截图:

funAddin.JPG

下图是客户端加载客户插件后的截图:

passiveMenu.JPG

下图是客户端插件提供服务的截图:

passiveForm2.JPG

经过上述的介绍,读者应该对开发服务端的功能插件和客户端插件有些了解了。快结束的时候,再为下篇blog开个头。当我们开发了客户端插件和服务端插件后,做调试是一项非常麻烦的工作,因为不仅要启动应用服务器,还要启动客户端主程序、功能服务器才行。为了简化这个过程,我实现了一个Bridge应用程序,只需要加载一pair插件(服务端插件和对应的客户插件),即可进行两个插件的调试,而不用在启动客户端、AS、FS了。

感谢关注!

上一篇:ESFramework介绍之(7)-- 服务器代理IServerAgent

转到:ESFramework 可复用的通信框架(序)




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值