一、摘要
場景:
項目比較大,不可能所有契約在一個類里來實現,可能會有上百個服務上千個契約,按照目前在網上搜索的結果是…..只能一個servicehost對應一個服務,某個mvp的建議是所有契約都放到一個類里來實現。。。我崩潰 。。。。。
想法:
也有人建議用 partial class的方式,把不同的契約分布到不同的CS文件里,雖然可能看上去好一點,但每回添加服務都得重編譯,我覺得太扯淡了 。。。。。
我希望是盡可能的把服務根據功能的不同按模塊划分開來,不同的服務做成不同的dll,以插件的形式注冊到一個表里,用servicehost來加載,不知大家有沒有好的建議 。
其實可以考慮用CSLA.NET的方案,只寫一個服務,它稱為統一入口,這個方式還是不錯的,就是他為解決並發,並發好像在博客園里有人寫了怎么實現,地址是 http://www.cnblogs.com/zgynhqf/archive/2010/07/01/1769228.html
現實:
說一千道一萬架不住我不會,我只能實現:
1、如何在Windows中Host多個WCF服務?
2、不能是多個Open方法,那樣太幼稚了 !3、 考慮到整個團隊都不會CSLA.NET,或OEA 就我會一點:) , 這個還的多謝周哥,和 胡總 的栽培了。
4、 整個團隊學習成本太高,只有不斷的分享,討論來加快進度了。
二、本文大綱
a、摘要 。
b、本文大綱 。
c、項目准備階段 。
d、主要核心代碼 。
e、達到的效果圖 。
三、項目准備階段
這個圖需要一點C# 基礎才能看的懂的:) 估計一上圖大家就看的懂(自己想象中……..)了。
這里主要是創建 Windows項目和 WCF Service Library 項目
Windows項目還需要引用WCF Service Library 項目
四、主要核心代碼
運行按鈕事件代碼:
1: private void button1_Click(object sender, EventArgs e)2: {
3: if (button1.Tag == "run")4: {
5: ServiceStart();
6: button1.Text = "停止(&X)";7: button1.Tag = "stop";8: }
9: else10: {11: button1.Text = "運行(&R)";12: button1.Tag = "run";13: }
14: }
15:
ServiceStart() 函數代碼:
1: private List<ServiceHost> serviceHosts = new List<ServiceHost>();2: private void ServiceStart()3: {
4:
5: #region 初?始?化ˉ serviceHosts6: if (serviceHosts != null)7: {
8: foreach (ServiceHost t in serviceHosts)9: {
10: if (t != null)11: t.Close();
12: }
13: }
14: else15: {16: serviceHosts = new List<ServiceHost>();17: }
18: #endregion19:
20: string serviceAddress = string.Format("net.tcp://{0}:{1}", "127.0.0.1", "8000");21:
22: Dictionary<Type,Type> sevtypes=new Dictionary<Type,Type>();23: sevtypes.Add(typeof(IService1),typeof(Service1));24: sevtypes.Add(typeof(IService2), typeof(Service2));25:
26: string endpointAddress = string.Empty;27: string tName = string.Empty;28: StringBuilder msgService = new StringBuilder();29: foreach (var item in sevtypes)30: {
31: tName = item.Key.Name.Substring(1);
32: endpointAddress = serviceAddress + tName;
33: if (!serviceAddress.EndsWith("/"))34: endpointAddress = string.Format("{0}/{1}", serviceAddress, tName);35: ServiceHost serviceHost = new ServiceHost(item.Value, new Uri(endpointAddress));36:
37: //加載元數據結點38: ServiceMetadataBehavior smb = new ServiceMetadataBehavior();39: serviceHost.Description.Behaviors.Add(smb);
40: serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");41: //加載NetTcpBinding結點42: NetTcpBinding netTcpBinding = new NetTcpBinding();43: netTcpBinding.Security.Mode = SecurityMode.None;44: netTcpBinding.ReceiveTimeout = TimeSpan.Parse("00:10:00");45: netTcpBinding.MaxBufferPoolSize = 2147483647;
46: netTcpBinding.MaxBufferSize = 2147483647;
47: netTcpBinding.MaxConnections = 10;
48:
49: netTcpBinding.ReaderQuotas.MaxDepth = 2147483647;
50: netTcpBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
51: netTcpBinding.ReaderQuotas.MaxArrayLength = 2147483647;
52: netTcpBinding.ReaderQuotas.MaxBytesPerRead = 2147483647;
53: netTcpBinding.ReaderQuotas.MaxNameTableCharCount = 2147483647;
54: netTcpBinding.MaxReceivedMessageSize = 2147483647;
55: serviceHost.AddServiceEndpoint(item.Key, netTcpBinding, endpointAddress);
56:
57: serviceHost.Opened += delegate58: {59: msgService.AppendLine(string.Format("{0}開始監聽 Uri 為 :{1}/mex", tName, endpointAddress.ToString()));60: };
61:
62: serviceHost.Open();
63: serviceHosts.Add(serviceHost);
64: }
65: this.textBox1.Text = msgService.ToString();66: }
67:
代碼約定:
每次增加服務和契約的時候都需要同時添加 Dictionary 要不然系統不會啟動新增加的服務。
服務地址:
net.tcp://127.0.0.1:8000/服務名/mex
上面的例子生成如下地址:
net.tcp://127.0.0.1:8000/Service1/mex
net.tcp://127.0.0.1:8000/Service2/mex
把 ”Dictionary ”改成配置文件形式,也就通用了。
網上的一個使用配置文件的例子:
配置文件如下:
1: <system.serviceModel>2: <services>3: <service name="ChinaQueue.Restaurant.WCFService.ServiceImplementations.PrinterSer" behaviorConfiguration="sb">4: <host>5: <baseAddresses>6: <add baseAddress="net.tcp://localhost:8081" />7: </baseAddresses>8: </host>9: <endpoint name="NetTcpBinding_IPrinterServer" address="PrinterSer" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IServer" contract="ChinaQueue.Restaurant.WCFService.ServiceContracts.IPrinterSer" />10: <endpoint name="MexTcpBinding_IPrinterServer" address="Printer" binding="mexTcpBinding" contract="IMetadataExchange" />11: </service>12: <service name="ChinaQueue.Restaurant.WCFService.ServiceImplementations.CounterSer" behaviorConfiguration="sb">13: <host>14: <baseAddresses>15: <add baseAddress="net.tcp://localhost:8081" />16: </baseAddresses>17: </host>18: <endpoint name="NetTcpBinding_ICounterServer" address="CounterSer" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IServer" contract="ChinaQueue.Restaurant.WCFService.ServiceContracts.ICounterSer" />19: <endpoint name="MexTcpBinding_ICounterServer" address="Counter" binding="mexTcpBinding" contract="IMetadataExchange" />20: </service>21: <service name="ChinaQueue.Restaurant.WCFService.ServiceImplementations.QuerySer" behaviorConfiguration="sb">22: <host>23: <baseAddresses>24: <add baseAddress="net.tcp://localhost:8081" />25: </baseAddresses>26: </host>27: <endpoint name="NetTcpBinding_IQueryServer" address="QuerySer" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IServer" contract="ChinaQueue.Restaurant.WCFService.ServiceContracts.IQuerySer" />28: <endpoint name="MexTcpBinding_IQueryServer" address="Query" binding="mexTcpBinding" contract="IMetadataExchange" />29: </service>30: </services>31: <behaviors>32: <serviceBehaviors>33: <behavior name="sb">34: <serviceMetadata />35: <serviceDebug includeExceptionDetailInFaults="true" />36: </behavior>37: </serviceBehaviors>38: </behaviors>39: <bindings>40: <netTcpBinding>41: <binding name="NetTcpBinding_IServer">42: <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />43: <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />44: <security mode="None">45: <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />46: <message clientCredentialType="Windows" />47: </security>48: </binding>49: </netTcpBinding>50: </bindings>51: <client>52: <endpoint address="net.tcp://localhost:8081/PrinterSer" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IServer" contract="ChinaQueue.Restaurant.WCFService.ServiceContracts.IPrinterSer" name="NetTcpBinding_IPrinterServer" />53: <endpoint address="net.tcp://localhost:8081/CounterSer" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IServer" contract="ChinaQueue.Restaurant.WCFService.ServiceContracts.ICounterSer" name="NetTcpBinding_ICounterServer" />54: <endpoint address="net.tcp://localhost:8081/QuerySer" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IServer" contract="ChinaQueue.Restaurant.WCFService.ServiceContracts.IQuerySer" name="NetTcpBinding_IQueryServer" />55: </client>56: </system.serviceModel>57:
以上是三個服務的配置,
然后在Main方法中Host這三個服務,以下是如何從配置文件中讀取服務並進行Host的System.ServiceModel.Configuration
1: Configuration conf = ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location);2: ServiceModelSectionGroup svcmod = (ServiceModelSectionGroup)conf.GetSectionGroup("system.serviceModel");3: foreach (ServiceElement el in svcmod.Services.Services)4: {
5: Type svcType = Type.GetType(el.Name + "," + "ChinaQueue.Restaurant.WCFService");6: if (svcType == null)7: throw new Exception("Invalid Service Type " + el.Name + " in configuration file.");8: ServiceHost aServiceHost = new ServiceHost(svcType);9: aServiceHost.Open();
10: }
11:
五、達到的效果圖
測試通過,有圖作證:
這個服務啟動成功了,客服端也測試通過,耶搞定。