我的想法是,当客户端主程序加载一个新的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接口继承。
“业务操作窗体”只有通过暴露的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获取城市的天气信息,而客户端插件则用于为用户提供这项服务。
先看服务端功能插件实现:
WeatherPreAddin
1 public class WeatherPreAddin :IFunAddin
2 {
3 private IContractHelper contractHelper = new ContractHelper() ;
4
5 public WeatherPreAddin()
6 {
7
8 }
9
10 #region IAddin 成员
11
12 public string ServiceName
13 {
14 get
15 {
16 // TODO: 添加 WeatherPreAddin.ServiceName getter 实现
17 return "天气预测服务";
18 }
19 }
20
21 public void OnLoading()
22 {
23 // TODO: 添加 WeatherPreAddin.OnLoading 实现
24 }
25
26 public string CatalogName
27 {
28 get
29 {
30 // TODO: 添加 WeatherPreAddin.CatalogName getter 实现
31 return "";
32 }
33 }
34
35 public int ServiceKey
36 {
37 get
38 {
39 // TODO: 添加 WeatherPreAddin.ServiceKey getter 实现
40 return 987 ;
41 }
42 }
43
44 public string Description
45 {
46 get
47 {
48 // TODO: 添加 WeatherPreAddin.Description getter 实现
49 return "";
50 }
51 }
52
53 public void BeforeTerminating()
54 {
55 // TODO: 添加 WeatherPreAddin.BeforeTerminating 实现
56 }
57
58 public bool Enabled
59 {
60 get
61 {
62 // TODO: 添加 WeatherPreAddin.Enabled getter 实现
63 return true;
64 }
65 set
66 {
67 // TODO: 添加 WeatherPreAddin.Enabled setter 实现
68 }
69 }
70
71 public float Version
72 {
73 get
74 {
75 // TODO: 添加 WeatherPreAddin.Version getter 实现
76 return 1;
77 }
78 }
79
80 public string AddinType
81 {
82 get
83 {
84 // TODO: 添加 WeatherPreAddin.AddinType getter 实现
85 return null;
86 }
87 }
88
89 public AddinAppendixInfo AddinAppendixInfo
90 {
91 get
92 {
93 // TODO: 添加 WeatherPreAddin.AddinAppendixInfo getter 实现
94 return null;
95 }
96 }
97
98 #endregion
99
100 #region IDataDealer 成员
101
102 public ESFramework.Network.NetMessage DealRequestMessage(ESFramework.Network.NetMessage reqMsg)
103 {
104
105 try
106 {
107 string url = "http://www.webservicex.net/globalweather.asmx" ;
108 string[] args = new string[2] ;
109 args[0] = this.contractHelper.GetStrFromStream(reqMsg.Body ,reqMsg.BodyOffset ,reqMsg.Header.MessageBodyLength) ;
110 args[1] = "China" ;
111 object result = WebServiceHelper.InvokeWebService(url ,"GetWeather" ,args) ;
112
113 XmlParser parser = new XmlParser(result.ToString() ,null) ;
114 WeatherPredictionContract body = new WeatherPredictionContract(this.contractHelper) ;
115 body.Pressure = parser.GetSingleLayerConfigValue("Pressure") ;
116 body.PressureLen = this.contractHelper.GetBytesFromStr(body.Pressure).Length ;
117
118 body.PreTime = parser.GetSingleLayerConfigValue("Time") ;
119 body.PreTimeLen = this.contractHelper.GetBytesFromStr(body.PreTime).Length ;
120
121 body.Temprature = parser.GetSingleLayerConfigValue("Temperature") ;
122 body.TempratureLen = this.contractHelper.GetBytesFromStr(body.Temprature).Length ;
123
124 body.Visbility = parser.GetSingleLayerConfigValue("Visibility") ;
125 body.VisbilityLen = this.contractHelper.GetBytesFromStr(body.Visbility).Length ;
126
127 body.Wind = parser.GetSingleLayerConfigValue("Wind") ;
128 body.WindLen = this.contractHelper.GetBytesFromStr(body.Wind).Length ;
129
130 reqMsg.Header.MessageBodyLength = body.GetStreamLength() ;
131
132 return new NetMessage(reqMsg.Header ,body.ToStream() ,0) ;
133
134 }
135 catch(Exception ee)
136 {
137 ee = ee ;
138 reqMsg.Header.MessageBodyLength = 0 ;
139 reqMsg.Header.Result = ServiceResultType.FailureByOtherCause ;
140 return new NetMessage(reqMsg.Header ,null);
141 }
142 }
143
144 #endregion
145 }
主要是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 ;
}
注意,theAgent成员即是通过Initialize传入的IServerAgent引用!
接着是IPassiveAddin实现:
WeatherPassiveAddin
1 public class WeatherPassiveAddin :IPassiveAddin
2 {
3 public WeatherPassiveAddin()
4 {
5 }
6
7 #region IPassiveAddin 成员
8
9 public Type AddinFormType
10 {
11 get
12 {
13 return typeof(AddinForm); //AddinForm即前面实现的业务窗体
14 }
15 }
16
17 #endregion
18
19 #region IAddin 成员
20
21 public string ServiceName
22 {
23 get
24 {
25 // TODO: 添加 WeatherPassiveAddin.ServiceName getter 实现
26 return "天气预测服务";
27 }
28 }
29
30 public void OnLoading()
31 {
32 // TODO: 添加 WeatherPassiveAddin.OnLoading 实现
33 }
34
35 public string CatalogName
36 {
37 get
38 {
39 // TODO: 添加 WeatherPassiveAddin.CatalogName getter 实现
40 return null;
41 }
42 }
43
44 public int ServiceKey
45 {
46 get
47 {
48 // TODO: 添加 WeatherPassiveAddin.ServiceKey getter 实现
49 return 987;
50 }
51 }
52
53 public string Description
54 {
55 get
56 {
57 // TODO: 添加 WeatherPassiveAddin.Description getter 实现
58 return null;
59 }
60 }
61
62 public void BeforeTerminating()
63 {
64 // TODO: 添加 WeatherPassiveAddin.BeforeTerminating 实现
65 }
66
67 public bool Enabled
68 {
69 get
70 {
71 // TODO: 添加 WeatherPassiveAddin.Enabled getter 实现
72 return true;
73 }
74 set
75 {
76 // TODO: 添加 WeatherPassiveAddin.Enabled setter 实现
77 }
78 }
79
80 public float Version
81 {
82 get
83 {
84 // TODO: 添加 WeatherPassiveAddin.Version getter 实现
85 return 1;
86 }
87 }
88
89 public string AddinType
90 {
91 get
92 {
93 // TODO: 添加 WeatherPassiveAddin.AddinType getter 实现
94 return null;
95 }
96 }
97
98 public AddinAppendixInfo AddinAppendixInfo
99 {
100 get
101 {
102 // TODO: 添加 WeatherPassiveAddin.AddinAppendixInfo getter 实现
103 return null;
104 }
105 }
106
107 #endregion
108 }
需要格外注意要实现AddinFormType属性,就是前面实现的“业务窗体”类型。
下图是功能服务器加载功能插件的截图:
下图是客户端加载客户插件后的截图:
下图是客户端插件提供服务的截图:
经过上述的介绍,读者应该对开发服务端的功能插件和客户端插件有些了解了。快结束的时候,再为下篇blog开个头。当我们开发了客户端插件和服务端插件后,做调试是一项非常麻烦的工作,因为不仅要启动应用服务器,还要启动客户端主程序、功能服务器才行。为了简化这个过程,我实现了一个Bridge应用程序,只需要加载一pair插件(服务端插件和对应的客户插件),即可进行两个插件的调试,而不用在启动客户端、AS、FS了。
感谢关注!
上一篇:ESFramework介绍之(7)-- 服务器代理IServerAgent
转到 :ESFramework 可复用的通信框架(序)