在学习和测试 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);
}
}

