在学习和测试 WCF 的时候,我通常使用 ChannelFactory 来代替 Client Proxy。
原因是方便,无须创建多个 Project / Class。
当然,在正式开发中还是使用 Client Proxy 要好些,毕竟 ChannelFactory 直接依赖于契约,违背了 SOA 边界隔离的原则。
使用 ChannelFactory 很简单,但完成方法后要及时调用 Close 或 Dispose,以便服务器及时释放服务实例。
其实该对象还实现了另外两个接口 —— IDisposable 和 ICommunicationObject。
下面是演示代码:
注意下面代码中使用代码创建了EndPoint终结点信息,因此它不需要你手工配置服务器和客户端的app.config。
[ServiceContract] public interface IContract { [OperationContract] void Test(int i); } public class MyService : IContract { public void Test(int i) { Console.WriteLine(i); } } public class WcfTest { public static void Test() { AppDomain.CreateDomain("Server1").DoCallBack(delegate { ServiceHost host = new ServiceHost(typeof(MyService), new Uri("http://localhost:8080/MyService")); host.AddServiceEndpoint(typeof(IContract), new BasicHttpBinding(), ""); host.Open(); }); IContract channel = ChannelFactory<IContract>.CreateChannel(new BasicHttpBinding(), new EndpointAddress("http://localhost:8080/MyService")); using (channel as IDisposable) { ICommunicationObject o = channel as ICommunicationObject; o.Opening += delegate { Console.WriteLine("Opening..."); }; o.Closing += delegate { Console.WriteLine("Closing..."); }; channel.Test(1); } } }
以上代码只是功能演示,实际使用时会有一些问题。
比如下面这个报错:
"在 ServiceModel 客户端配置部分中,找不到名称“CalMethod1”和协定“ClassLibrary1.ICal”的终结点元素。
这可能是因为未找到应用程序的配置文件,或者是因为客户端元素中找不到与此名称匹配的终结点元素。"
其原因是你在客户端没有配置好app.config。
当你使用VS的添加服务引用时,系统会自动在客户端的app.config里写上必要的配置,你会有一种错觉是不需要配置客户端的app.config了。
下面是客户端的配置。
注意其中那个endpoint终结点务必要配置一个name属性。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <netNamedPipeBinding> <binding name="CalMethod" /> </netNamedPipeBinding> </bindings> <client> <endpoint address="net.pipe://localhost/CalMethod" binding="netNamedPipeBinding" bindingConfiguration="CalMethod" contract="ClassLibrary1.ICal" name="CalMethod"> <identity> <userPrincipalName value="liuxiaoyong@samsuncn.com" /> </identity> </endpoint> </client> </system.serviceModel> </configuration>
下面是Host端的配置:
可以看到它的endpoint终结点是可以不要name属性的。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="CalMethod"> <serviceDebug httpHelpPageEnabled="false"/> <serviceMetadata httpGetEnabled="false"/> <serviceTimeouts transactionTimeout="00:10:00"/> <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="ClassLibrary1.CalMethod" behaviorConfiguration="CalMethod"> <host> <baseAddresses> <add baseAddress="net.pipe://localhost/CalMethod"/> </baseAddresses> </host> <endpoint address="" binding="netNamedPipeBinding" bindingConfiguration="" contract="ClassLibrary1.ICal"/> <endpoint address="mex" binding="mexNamedPipeBinding" contract="IMetadataExchange"/> </service> </services> </system.serviceModel> </configuration>
客户端代码:
using ClassLibrary1; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { ChannelFactory<ICal> fac = null; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { fac = new ChannelFactory<ICal>("CalMethod1"); } private void button1_Click(object sender, EventArgs e) { ICal proxy = fac.CreateChannel(); using (proxy as IDisposable) { ICommunicationObject com = proxy as ICommunicationObject; com.Opening += delegate { richTextBox1.AppendText($"Opening...\n"); }; com.Closing += delegate { richTextBox1.AppendText($"Closing...\n"); }; richTextBox1.AppendText($"{proxy.Cal(10, 5)}\n"); } } } }
Host端代码:
using ClassLibrary1; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { private ServiceHost host = null; public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { try { host.Open(); richTextBox1.AppendText("服务已经打开"); } catch (Exception ex) { MessageBox.Show(ex.Message); } } private void button2_Click(object sender, EventArgs e) { try { host.Close(); richTextBox1.AppendText("服务已经关闭"); } catch { host.Abort(); } } private void Form1_Load(object sender, EventArgs e) { host = new ServiceHost(typeof(CalMethod)); this.Disposed += delegate { host.Close(); }; } } }
service端代码:
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; namespace ClassLibrary1 { [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class CalMethod : ICal { int ICal.Cal(int a, int b) { return a + b; } } }
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; namespace ClassLibrary1 { [ServiceContract( SessionMode = SessionMode.Allowed)] public interface ICal { [OperationContract] int Cal(int a, int b); } }

