wcf学习三(手工编写wcf服务)
wcf并不限于使用Http协议。在iis上使用wcf,就让wcf拥有了webservice一样的软肋,可以说是扼杀其特长的用法。通常我们可以利用手工服务编写的wcf服务来承载我们需求的应用
直接给例子:
1:建一个名为:LearingWCF的解决方案 2:在该解决方案下建立一个名为:WCFServer的控制台应用程序 然后在该控制台下面添加一个System.ServiceModel命名空间的引用。 与wcf有关的类都位于整个命名空间下。应该编写的代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace WCFServer
{
[ServiceContract]
public interface IShopping
{
[OperationContract]
float GetPrice(float price, int count);
}
public class ShoppingService:IShopping {
public float GetPrice(float price, int count)
{
return price * count * 0.9f;
}
}
class Program
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(ShoppingService), new Uri("http://localhost:12345/shoppingWCF"));
host.AddServiceEndpoint(typeof(IShopping), new BasicHttpBinding(), "");
//ServiceHost host = new ServiceHost(typeof(ShoppingService));
host.Open();
Console.WriteLine("侦听服务已经在本机12345端口开启");
Console.ReadLine();
host.Close();
}
}
}
解释1:其中每一个功能都称之为一个"操作契约",这些,均需要显示使用相应的关键字标志在接口和方法上。即:[ServiceContract]和[OperationContract]特性,然后 我们使用一个类继承自该接口 以方便"实现"这些服务
2:在main函数中,首先声明ServiceHost对象,他代表提供主机服务。有三个构造函数 可以跳转到msdn去了解属性和方法:a:ServiceHost 初始化 ServiceHost 类的新实例. b:ServiceHost(Object, Uri()) 使用服务的实例及其指定的基址初始化 ServiceHost 类的新实例。
c: ServiceHost(Type, Uri()) 使用服务的类型及其指定的基址初始化 ServiceHost 类的新实例。
上面的代码中 ServiceHost host = new ServiceHost(typeof(ShoppingService), new Uri("http://localhost:12345/shoppingWCF")); 第一个参数指示了购物服务(ShoppingService)发布给用户,第二个参数指明了该服务发布在本机的12345端口,服务名为:shoppingWCF 此时依然使用的是http协议。
host.AddServiceEndpoint(typeof(IShopping), new BasicHttpBinding(), ""); //添加端点 指定了哪一个服务,绑定于那个端口 第三个为空 指的是端口与主机发布的端口相同。 open 开启监听 close方法停止监听
很简单的服务端就这样了 下面写一个更简单的客户端 直接在改解决方案下添加一个winform项目起名为:WCFClient.界面如图:
一样的引入System.ServiceModel 编写代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace WCFClient
{
[ServiceContract]
public interface IShopping
{
[OperationContract]
float GetPrice(float price, int count);
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnRun_Click(object sender, EventArgs e)
{
ChannelFactory<IShopping> chFactory = new ChannelFactory<IShopping>(new BasicHttpBinding(), new EndpointAddress("http://localhost:12345/shoppingWCF"));
IShopping wcfclient = chFactory.CreateChannel();
float result = wcfclient.GetPrice(55.99f,20);
MessageBox.Show(result.ToString());
}
}
}
ChannelFactory类 指定网络通讯的方式—我们称之为"绑定",以及向谁去通讯—我们称之为"端点",还有请求的服务契约
ChannelFactory<IShopping> chFactory = new ChannelFactory<IShopping>(new BasicHttpBinding(), new EndpointAddress("http://localhost:12345/shoppingWCF"));
//指定绑定的为基础http绑定,端点为服务端的12345端口,而契约则是我们的IShopping接口
在解决方案上点击右键,属性,将解决方案中的两个项目设置为一起启动 如图
然后按下F5 就会出现效果了
反思一下 将一个接口写两遍是不是有点怪啊 那就改写一下 如图吧:
其中接口的方法如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System.ServiceModel;
namespace Contract
{
[ServiceContract]
public interface IShopping
{
[OperationContract]
float GetPrice(float price, int count);
}
}
然后在客户端和服务端添加引用就可以了,后面针对这个问题还会继续解决的。
//下面我们针对问题来解决 就是利用幕后英雄--配置文件 可以利用微软为我们准备了一个设计器vs2010如图
然后选择新建服务
浏览服务端的exe文件如图:
然后打开下一步
继续下一步如图选择服务约定 如图
然后下一步就不截图了 直接写了 选择通信模式 (http的) 继续下一步 选择互操作模式 选择第一个(基本web服务互操作性) 继续下一步总结地址为:
http://localhost:12345/shoppingWCF 然后下一步 跳转到完成 把生成的文件保存到WCFServer的跟目录下面
app.config的默认名称不要修改 里面的代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<services>
<service name="WCFServer.ShoppingService">
<endpoint address="http://localhost:12345/shoppingWCF" binding="basicHttpBinding"
bindingConfiguration="" name="shoppingWCF" contract="Contract.IShopping" />
</service>
</services>
</system.serviceModel>
</configuration>
由于配置用指定了端点,所以源代码就没有必要指定了,修改后的服务器代码如下
static void Main(string[] args)
{ //ServiceHost host = new ServiceHost(typeof(ShoppingService), new Uri("http://localhost:12345/shoppingWCF")); //host.AddServiceEndpoint(typeof(IShopping), new BasicHttpBinding(), ""); ServiceHost host = new ServiceHost(typeof(ShoppingService)); host.Open(); Console.WriteLine("侦听服务已经在本机12345端口开启"); Console.ReadLine(); host.Close(); }
执行之后效果一样,但是代码量小了,易读性提高了,以后修改只用修改配置文件的端点就可以 或者修改tcp/ip协议 无需对程序做任何修改
//习惯之后可以直接写配置文件 网上介绍的很多 这里就不解释了 知道怎么来的 然后去写就容易多了
既然服务端可以通过配置文件解决来实现代码的简化,并提高部署的灵活性,那么客户端肯定也可以拥有同样的功能 也就是所谓的元数据交换点,就是将 地址 绑定 契约 公布出来 需要使用
System.ServiceModel.Description这个命名空间
服务端的代码改写成
ServiceHost host = new ServiceHost(typeof(ShoppingService));
//元数据交换行为 ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); //控制服务元数据和相关信息发布 behavior.HttpGetEnabled = true; host.Description.Behaviors.Add(behavior); host.AddServiceEndpoint(typeof(IMetadataExchange),MetadataExchangeBindings.CreateMexHttpBinding(),"mex");//后面的就一样了
然后在利用wcf服务配置编译器就是配置如图:添加一个终结点shoppingWCFMex
然后添加一个服务行为如图
然后保存 生成的配置文件里面的代码就成了这样的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="mexBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="mexBehavior" name="WCFServer.ShoppingService">
<endpoint address="http://localhost:12345/shoppingWCF" binding="basicHttpBinding"
bindingConfiguration="" name="shoppingWCF" contract="Contract.IShopping" />
<endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
name="shoppingWCFMex" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:12345/shoppingWCF" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
当然习惯了 可以直接写配置文件就可以了 此时服务的代码又变成了最短的
代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
static void Main(string[] args)
{
//ServiceHost host = new ServiceHost(typeof(ShoppingService), new Uri("http://localhost:12345/shoppingWCF"));
//host.AddServiceEndpoint(typeof(IShopping), new BasicHttpBinding(), "");//1最原始
ServiceHost host = new ServiceHost(typeof(ShoppingService));
//ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); //控制服务元数据和相关信息发布
//behavior.HttpGetEnabled = true;
//host.Description.Behaviors.Add(behavior);
//host.AddServiceEndpoint(typeof(IMetadataExchange),MetadataExchangeBindings.CreateMexHttpBinding(),"mex"); 2代码的改变
host.Open();
Console.WriteLine("侦听服务已经在本机12345端口开启");
Console.ReadLine();
host.Close();
最后处理客户端的事情 肯定不用添加接口的引用了 利用"代理类"来处理 也不需要管道了
在vs2010的程序中找到visual studio Tools —— visual studio 命令提示,运行之后输入
svcutil http://loacalhost:12345/shoppingWCF/mex/ -config:d:\app.config -out:d:\code.cs
如图:
然后将生成的code.cs 和app.config复制到到客户端下面(上面的app不小心写成aap)
然后客户端的后台代码变成了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace WCFClient
{
//[ServiceContract]
//public interface IShopping
//{
// [OperationContract]
// float GetPrice(float price, int count);
//}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnRun_Click(object sender, EventArgs e)
{
//ChannelFactory<IShopping> chFactory = new ChannelFactory<IShopping>(new BasicHttpBinding(), new EndpointAddress("http://localhost:12345/shoppingWCF"));
//IShopping wcfclient = chFactory.CreateChannel();
//float result = wcfclient.GetPrice(55.99f,20);
//MessageBox.Show(result.ToString());
ShoppingClient client = new ShoppingClient();
float result = client.GetPrice(55.99f,20);
MessageBox.Show(result.ToString());
}
}
}
注释的就是进化前的代码
效果就出来了 就是这样啦