WCF服务三:svc文件详解
在前面的文章中讲述过WCF服务的宿主程序主要包括:三种,在那篇文章中,简单的描述了如何把一个WCF服务寄宿到IIS上面,这篇文章中将具体讲述如何把一个WCF服务寄宿到IIS上面。
一、新建一个WCF服务应用程序:
文件->新建->项目:选择WCF下面的WCF服务应用程序

二、分析WcfSvcDemo项目,该项目的结构如下:

在该项目中,会默认生成一个IService1.cs的文件和Service1.svc文件。Service1.svc文件封装的就是提供给客户端的服务引用。
首先查看IService1.cs文件,从名字上面就可以看出这是一个接口文件,里面定义了一个接口IService1,接口上面使用了ServiceContract,意思是把这个接口声明为服务契约,服务契约是对客户端而言的,就是这个接口可以暴露出来让客户端可以看见。接口里面定义了两个方法,每个方法上面都使用了[OperationContract],意思是把这两个方法声明为操作契约。只有把接口里面的方法声明为操作契约,在客户端里面才可以看到相应的方法,否则在客户端里面看不到在接口里面定义的方法。
在来看Service.svc文件,可以看到下面有一个Service.svc.cs文件,这个文件里面定义了一个继承IService1接口的类Service1,并实现了IService1接口里面的方法。
删除Service.svc.cs文件,可以查看Service.svc文件,该文件里面就一行代码;
<%@ ServiceHost Language="C#" Debug="true" Service="WcfSvcDemo.Service1" CodeBehind="Service1.svc.cs" %>
这里面有两个重要的参数:Service和CodeBehind。Service是属性值是WCF的服务实现类的完全限定名。CodeBehind是服务实现类所在的文件名。在运行的时候,宿主程序从svc文件中的Service属性得到WCF服务的完全限定名,然后从配置文件中找到同名的servicce,进而找到所有的EndPoint,并根据其属性进行实例化。
配置文件中的Service名字必须是Service类名的完全限定名(即Namespace.classname),EndPoint的Contract必须是Service接口的完全限定名。否则,程序就无法从程序集中找到相应的类进行加载。
注意:如果要修改接口实现类的名称,必须使用“重构”的方式进行修改,因为只有利用“重构”的方式修改Servie类名的时候,.svc文件里面Service的属性值才会被修改,利用其它方式修改类名,.svc文件里面Service的属性值会保留原值,这样在运行的时候,根据svc里面Service的属性值查找不到相应的类,程序就会报错。
svc文件里面还有一个重要的参数:ServiceHostFactory。ServiceHostFactory旨在解决从IIS或WAS中访问自定义ServiceHost的问题。因为从ServiceHost派生的自定义宿主是动态配置的并且可能为各种类型,所以宿主环境从不会直接将其实例化。相反,WCF使用工厂模式提供宿主环境和服务的具体类型之间的间接层。除非进行通知,否则它使用返回ServiceHost的实例的ServiceHostFactory的默认实现。(在新建的svc文件中默认实现就是CodeBehind属性的值)。但也可以通过在@ServiceHost指令中指定工厂实现的CLR类型名称来提供自己的工厂(用于返回派生宿主)。
下面是用于返回派生的ServiceHost的自定义ServiceHostFactory:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation; namespace Public.CustomService
{
public class CustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
CustomServiceHost customServiceHost = new CustomServiceHost(serviceType, baseAddresses);
return customServiceHost;
}
}
}
其中CustomServiceHost是自定义的继承自ServiceHost的类,用于读取配置文件的配置,CustomServiceHost类的定义如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation; namespace Public.CustomService
{
public class CustomServiceHost :ServiceHost
{
public CustomServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
//加载Web.config的配置
log4net.Config.XmlConfigurator.Configure();
}
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
}
}
}
若要使用此工厂,而不使用默认工厂,则应该在@ServiceHost指令中提供相应的类型名称:
<%@ ServiceHost Service="CustomSvcDemo.DatabaseService" Factory="Public.CustomService.CustomServiceHostFactory" %>
其中Service是实现类的完全限定名,Factory是自定义ServiceHostFactory的完全限定名,Public是一个dll文件。
若要使用此工厂,而不使用默认工厂,则应该在@ServiceHost指令中提供相应的类型名称:
尽管对于从CreateServiceHost返回的ServiceHost可以执行什么操作没有技术限制,但建议您尽可能使工厂实现简单化。如果有大量的自定义逻辑,最好将这些逻辑放入宿主内而不是工厂内,以便可以重用它们。
应在这里提及另一个承载API的层。WCF还具有ServiceHostBase和ServiceHostFactoryBase,可从中分别派生ServiceHost和ServiceHostFactory。对于您必须通过自己的自定义创建来交换元数据系统的大型组件的更高级方案,存在上述这些特性。
下面通过两个具体的示例程序分别实现上面描述的默认工厂和自定义工厂。
三、使用默认工厂方式
删除新建项目时自动创建的IService1.cs和Service1.svc文件,然后添加一个svc文件,在项目上面右键->添加->新建项:
在新建项里面选择web里面的WCF服务,命名为MyService:

点“添加”,除了创建MyService.svc文件以外,还会自动创建一个名为IMyService.cs的接口文件,MyService.svc.cs里面的MyService默认实现IMyService接口.
删除IMyService接口里面自动生成的方法,添加一个GetCurrentTime的方法,用来返回当前的时间,IMyService接口定义如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace WcfSvcDemo
{
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IMyService”。
[ServiceContract]
public interface IMyService
{
/// <summary>
/// 获取当前时间
/// </summary>
/// <returns></returns>
[OperationContract]
DateTime GetCurrentTime();
}
}
4、MyService.svc.cs里面的MyService类实现IMyService接口,MyService类定义如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace WcfSvcDemo
{
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“MyService”。
// 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 MyService.svc 或 MyService.svc.cs,然后开始调试。
public class MyService : IMyService
{
/// <summary>
/// 返回当前时间
/// </summary>
/// <returns></returns>
public DateTime GetCurrentTime()
{
return DateTime.Now;
}
}
}
5、修改配置文件,增加service、binding等节点,修改后的配置文件如下:
<?xml version="1.0"?>
<configuration>
<appSettings/>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<httpRuntime/>
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="metadataBehavior" name="WcfSvcDemo.MyService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="Contract" name="MyService" contract="WcfSvcDemo.IMyService"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="Contract" closeTimeout="00:00:05" openTimeout="00:00:05" receiveTimeout="11:00:00" sendTimeout="11:00:00" maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" transferMode="Buffered">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
</binding>
</basicHttpBinding>
</bindings>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
若要在调试过程中浏览 Web 应用程序根目录,请将下面的值设置为 True。
在部署之前将该值设置为 False 可避免泄露 Web 应用程序文件夹信息。
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
主要是修改service节点里面的name是服务实现类的完全限定名,contract是服务接口的完全限定名。
6、把WCF服务部署到IIS上面
在IIS上面网站->添加网站:

配置网站名称、路径、IP地址和端口:

网站配置完成以后,浏览.svc文件,验证网站是否配置成功,如出现下面的截图,说明网站配置成功:

7、创建代理类
客户端引用WCF的时候一般是静态引用,直接添加服务引用,这种方式如果IP地址和端口号变了,需要用代码重新编译然后在部署,这样不方便。这里使用svcutil代理类的方式进行客户端的调用。
使用svcutil生成代理类:

新建一个项目,选择类库项目,把刚才生成的类文件添加到类库项目中,项目结构如下:

在类库项目中新添加一个类,命名为:MyServiceProxy,使用这个类来调用代理类,MyServiceProxy类的定义如下:
using Public.ConfigBinding;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks; namespace MyProxyService
{
public class MyServiceProxy
{
private static MyServiceClient _databaseService; private static MyServiceClient DatabaseService
{
get
{
if (_databaseService == null)
{
string sApServer1 = ConfigurationManager.AppSettings["ApServer1"]; if (sApServer1 == null)
{
_databaseService = new MyServiceClient();
}
else
{
EndpointAddress endPointAddr = new EndpointAddress(string.Format("{0}/MyService.svc", sApServer1));
_databaseService = new MyServiceClient(HttpBinding.BasicHttpBinding, endPointAddr);
}
} if (_databaseService.State == CommunicationState.Faulted)
{
string sApServer2 = ConfigurationManager.AppSettings["ApServer2"]; if (sApServer2 == null)
{
_databaseService = new MyServiceClient();
}
else
{
EndpointAddress endPointAddr = new EndpointAddress(string.Format("{0}/MyService.svc", sApServer2));
_databaseService = new MyServiceClient(HttpBinding.BasicHttpBinding, endPointAddr);
}
} return _databaseService;
}
} /// <summary>
/// 返回当前时间
/// </summary>
/// <returns></returns>
public static DateTime GetCurrentTime()
{
return DatabaseService.GetCurrentTime();
}
}
}
ApServer1和ApServer2是在配置文件中配置的IP地址和端口号,这样如果IP地址和端口号变了,只需要修改配置文件就可以。
GetCurrentTime()方法是调用的代理类里面的方法,把该方法定义为静态方法。
8、创建客户端调用
在解决方案中,新建一个winform程序,界面上面只有一个button按钮,点击按钮,弹出当前时间。需要添加对MyProxyService.dll文件的引用,在配置文件中增加ApServer1和ApServer2两个节点,配置文件如下:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="ApServer1" value="http://127.0.0.1:8090"/>
<add key="ApServer2" value="http://127.0.0.1:8090"/>
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
button按钮事件代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MyProxyService; namespace WinformClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private void btn_CurrentTime_Click(object sender, EventArgs e)
{
DateTime dtNow = MyServiceProxy.GetCurrentTime();
MessageBox.Show("当前时间:" + dtNow.ToString());
}
}
}
点击按钮后,运行结果如下:

四、使用自定义工厂的方式
1、新添加一个WCF服务,命名为CustomService,把默认生成的CustomService.svc.cs文件删掉,重新添加一个类:CustomService,该类继承自生成的ICustomService接口,项目结构如下:

修改CustomService.svc文件:
<%@ ServiceHost Service="WcfSvcDemo.CustomService" Factory="Public.CustomService.CustomServiceHostFactory" %>
CustomServiceHostFactory是在另外的Public.dll里面创建的工厂类,用来返回ServiceHost,Public.dll的项目结构如下:

CustomServiceHost类继承自ServiceHost类,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation; namespace Public.CustomService
{
public class CustomServiceHost :ServiceHost
{
public CustomServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
//加载Web.config的配置
log4net.Config.XmlConfigurator.Configure();
}
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
}
}
}
CustomServiceHostFactory是工厂类,继承自ServiceHostFactory,用来返回ServiceHost,代码如下;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation; namespace Public.CustomService
{
public class CustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
CustomServiceHost customServiceHost = new CustomServiceHost(serviceType, baseAddresses);
return customServiceHost;
}
}
}
HttpBinding代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Xml; namespace Public.ConfigBinding
{
public class HttpBinding
{
private static BasicHttpBinding _BasicHttpBinding; public static BasicHttpBinding BasicHttpBinding
{
get
{
if (_BasicHttpBinding == null)
{
_BasicHttpBinding = new BasicHttpBinding(); // 接收的讯息大小上限,默认值为65,536字节,
// 目前设定1k * 512,如果资料量大于这个值,请提出讨论,ex:8000笔资料大概128k
_BasicHttpBinding.MaxReceivedMessageSize = * * ; // 由于回传String长度过长在反串行化时会出错!
// 所以放大最大字符串长度
_BasicHttpBinding.ReaderQuotas.MaxStringContentLength = * ;
_BasicHttpBinding.ReaderQuotas.MaxArrayLength = * ;
_BasicHttpBinding.SendTimeout = new TimeSpan(, , ); } return _BasicHttpBinding;
}
}
}
}
把CustomService.svc部署到IIS上面、创建代理类的方法、客户端调用和默认工厂里面的方法一样,此处不在描述。
项目代码下载路径:https://files.cnblogs.com/files/dotnet261010/WCFStudyDemo.rar
WCF服务三:svc文件详解的更多相关文章
- Mybatis(三) 映射文件详解
前面说了全局配置文件中内容的详解,大家应该清楚了,现在来说说这映射文件,这章就对输入映射.输出映射.动态sql这几个知识点进行说明,其中高级映射(一对一,一对多,多对多映射)在下一章进行说明. 一.输 ...
- IIS配置问题:WCF服务打开svc文件报错:请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理
在参考网上多个教程后,我用IIS配置的网站终于能正常打开了,但是很快就发现了新的问题,在打开WCF服务中的svc文件时报错: HTTP 错误 404.17 - Not Found请求的内容似乎是脚本, ...
- PE文件详解(三)
本文转自小甲鱼的PE文件详解系列传送门 PE文件到内存的映射 在执行一个PE文件的时候,windows 并不在一开始就将整个文件读入内存的,二十采用与内存映射文件类似的机制. 也就是说,windows ...
- [转]AndroidManifest.xml文件详解
转自:http://www.cnblogs.com/greatverve/archive/2012/05/08/AndroidManifest-xml.html AndroidManifest.xml ...
- VSFTPD全攻略(/etc/vsftpd/vsftpd.conf文件详解)
/etc/vsftpd/vsftpd.conf文件详解,分好类,方便大家查找与学习 #################匿名权限控制############### anonymous_enable=YE ...
- server.properties 文件详解
[转载]:server.properties 文件详解 # 每一个Broker在集群中的唯标识.即使Broker的IP地址发生了变化,broker.id只要没变,则不会影响consumers的消息情况 ...
- Liunx中fstab文件详解
Liunx中fstab文件详解 /etc/fstab是用来存放文件系统的静态信息的文件.位于/etc/目录下,可以用命令less /etc/fstab 来查看,如果要修改的话,则用命令 vi /etc ...
- 5、SAMBA服务一:参数详解
①:SAMBA服务一:参数详解 ②:SAMBA服务二:配置实例 一.SAMBA简介 samba指SMB(Server Message Block,服务器信息块)协议在网络上的计算机之间远程共享Linu ...
- 【转】linux中inittab文件详解
原文网址:http://www.2cto.com/os/201108/98426.html linux中inittab文件详解 init的进程号是1(ps -aux | less),从这一点就能看出, ...
随机推荐
- [JS Compose] 7. Ensure failsafe combination using monoids
monoids is a semi-group with a neutral element. A semigroup, it does not have an element to return s ...
- Reduce 优化(mapr)
1.合理设计桶的大小,插入桶的时候,桶的数目和reduce的数目一致,结合map的输出大小合理设置桶的大小,否则在reduce阶段就会非常慢. 2.查看reduce的copy的速率,如果map out ...
- 算法笔记_035:寻找最小的k个数(Java)
目录 1 问题描述 2 解决方案 2.1 全部排序法 2.2 部分排序法 2.3 用堆代替数组法 2.4线性选择算法 1 问题描述 有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 2 ...
- Box layout
Layout management with layout classes is much more flexible and practical. It is the preferred way t ...
- taro 填坑之路(一)taro 项目回顾
(1)像素写法 PX -- 大写,否则会自动成rem (2)拿取列表第一条数据 let { activity:[firstItem] } = this.state; (3)使用props 需要设置默认 ...
- 如何使用Android MediaStore裁剪大图片
译者按:在外企工作的半年多中花了不少时间在国外的网站上搜寻资料,其中有一些相当有含金量的文章,我会陆陆续续翻译成中文,与大家共享之.初次翻译,“信达雅”三境界恐怕只到信的层次,望大家见谅! 这篇文章相 ...
- MPEG-4 压缩编码标准
文章转自:http://www.cnblogs.com/CoderTian/p/8477021.html 1.MPEG-4标准概述 与MPEG1和MPEG2标准相比,MPEG-4 更加注重多媒体系统的 ...
- python --对象的属性
转自:http://www.cnblogs.com/vamei/archive/2012/12/11/2772448.html Python一切皆对象(object),每个对象都可能有多个属性(att ...
- DropDownList 添加“请选择”
this.DDLUsers.Items.Insert(0, new ListItem("", ""));
- 用Visual studio2012在Windows8上开发内核驱动监视线程创建
在Windows NT中,80386保护模式的“保护”比Windows 95中更坚固,这个“镀金的笼子”更加结实,更加难以打破.在Windows 95中,至少应用程序I/O操作是不受限制的,而在Win ...