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

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

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

IPassiveAddin直接从IAddin继承,仅仅增加了一个属性AddinFormType,这个属性就是前面说的客户端插件提供的“业务操作窗体”。“业务操作窗体”必须从IPassiveAddinForm接口继承。
“业务操作窗体”只有通过暴露的Initialize方法获取IServerAgent引用后,才能发送请求获取结果。Initialize方法的第二个参数说明当前时哪个用户在操作,这样客户端插件在构建请求消息时,需要将userID填充到请求消息的消息头中去,这样服务器才会知道这个消息的来源。

下面的代码说明了客户端主程序是如何加载IPassiveAddin的:
1privatevoidLoadPassiveAddins()
2{
3this.lIToolStripMenuItem_addin.DropDownItems.Clear();
4
5stringdirectory=System.IO.Directory.GetParent(System.Windows.Forms.Application.ExecutablePath).FullName;
6this.addinManagement.LoadAllAddins(directory,true);
7
8foreach(IAddinaddininthis.addinManagement.AddinList)
9{
10IPassiveAddinpassiveAddin=addinasIPassiveAddin;
11if(passiveAddin!=null)
12{
13ToolStripItemitem=newToolStripMenuItem(passiveAddin.ServiceName,null,newEventHandler(this.OnAddinMenuClicked));
14item.Tag=passiveAddin;
15this.lIToolStripMenuItem_addin.DropDownItems.Add(item);
16}
17}
18}
19
20privatevoidOnAddinMenuClicked(objectsender,EventArgse)
21{
22try
23{
24ToolStripItemitem=(ToolStripItem)sender;
25IPassiveAddinpassiveAddin=(IPassiveAddin)item.Tag;
26FormaddinForm=(Form)Activator.CreateInstance(passiveAddin.AddinFormType);
27((IPassiveAddinForm)addinForm).Initialize(this.tcpServerAgent,this.currentUserID);
28addinForm.Show();
29}
30catch(Exceptionee)
31{
32ee=ee;
33}
34}

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

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

ESFramework介绍之(8)-- 客户端插件IPassiveAddinESFramework介绍之(8)-- 客户端插件IPassiveAddinWeatherPreAddin
<!--<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,然后返回给客户端。

接下来看客户端插件的实现,分为两步:首先是“业务操作窗体”界面设计
ESFramework介绍之(8)-- 客户端插件IPassiveAddin

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

ESFramework介绍之(8)-- 客户端插件IPassiveAddinESFramework介绍之(8)-- 客户端插件IPassiveAddinbutton1_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实现:
ESFramework介绍之(8)-- 客户端插件IPassiveAddinESFramework介绍之(8)-- 客户端插件IPassiveAddinWeatherPassiveAddin
<!--<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属性,就是前面实现的“业务窗体”类型。

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

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

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

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

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

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

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

感谢关注!

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

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