只使用代码而不用配置文件的情况不适合IIS为宿主的情况,IIS宿主必须使用配置文件配置WCF的ServiceHost.
1、 服务端
1.1. 准备Contract和实现Contract的服务
很简单的一个Contract(Interface)和实现这个Contract(实现这个接口的类)的服务。
这是VS2005中使用add new item,选WCF Service后自动生成的一个模板例子服务代码。
[ServiceContract()]
public interface IService
{
[OperationContract]
string MyOperation1(string myValue);
}
public class Service : IService
{
public string MyOperation1(string myValue)
{
return "Hello: " + myValue;
}
}
1.2. 建立ServiceHost
一般使用public ServiceHost(Type serviceType, params Uri[] baseAddresses)构造方法建立ServicesHost.
参数:
Type serviceType —— 为实现了某些Contract的类的类型,为这个服务主机要host的服务。
params Uri[] baseAddresses —— 为任意数量的baseAddress.
Uri baseAddress = new Uri("http://localhost:8080/WCFService/Service");
//Instantiate new ServiceHost
myServiceHost = new ServiceHost(typeof(Service), baseAddress);
一个ServiceHost内只能驻留一个Service类,但是这个Service类可以实现多个Contract,每个Contract都能通过一个或多个(不同的bind)Endpoint向客户端暴露。
进程、应用程序域和ServiceHost
Dotnet出现之前,资源的分配是以进程为单位,进程是应用程序的安全边界,进程之间不能直接访问,一个进程的崩溃也不会直接影响到别的进程。
但是,进程有个缺点,为了维护进城的安全上下文,耗费的资源很大。
后来引入了线程,一个进程中可以包含多个线程,同一进程里的线程共享资源、切换方便,但是线程不具有隔离性,一个线程的崩溃将会影响到其他线程。
Dotnet引入了应用程序域,是介于进程和线程之间的逻辑概念,它既有进程的安全隔离性的优点,又有线程轻巧快捷的特性。应用程序域跟进程一样,一个应用程序域不能直接访问另一个应用程序域的资源,一个应用程序域的崩溃也不会影响其他应用程序域。同时应用程序域占用的资源比进程少的多,应用程序直接的切换也很快捷。
一个进程中可以包含多个应用程序域,一个应用程序域内有可以包含多个线程。
所有的win可执行文件(exe、dll等等)的开头都是一个被称作(Portable Executable)结构,dotnet的可执行文件同样也是用了这个PE头(结构同以前的兼容,只是增加了些内容),下面是dotnet的PE主要包含的信息:
运行这个可执行文件要求的最低CLR版本号
是否使用了强名称
程序的入口地址
可执行文件的元数据(metadata)
简单的dotnet的exe可执行文件的载入过程:
windows程序载入器(os loader)读取exe文件的PE头,获取入口地址,exe入口地址其实是个跳转指令指向mscoree.dll中的_corexemain函数。
_corexemain函数实际上是个入口程序,一般被称作shim(填隙物)。由这个入口程序来决定使用哪个类型的CLR(服务器类型或工作站类型),和什么版本的CLR来运行这个exe.
确定了使用哪个CLR后,动态载入这个CLR,把控制权交给CLR.
欢迎进入.NET社区论坛,与200万技术人员互动交流 >>进入
CLR拿到控制权后,首先创建一个工作进程,以便在进程内创建应用程序域。
CLR创建工作进程后,首先会创建一个缺省应用程序域,这个缺省应用程序域一般不用来加载用户代码。之后,开始在在新的应用程序域中加载用户代码,并运行。
具体到承载WCF的console应用,是个dotnet的exe程序,运行后被CLR加载到一个应用程序域后,ServiceHost就在这个应用程序内运行
在一个Application Domain中可以实例化多个 ServiceHost 实例,但每个应用程序域内只有一个 ServiceHost 实例更便于操作。您可以在一个宿主内使用多个端点公开多个服务接口。
1.3. 给ServiceHost添加Endpoint
Endpoint是直接暴露给客户端就行通讯的接口,经典的一个Endpoint可以用ABC来描述,即address – 这个Endpoint对外的访问地址,binding – 这个Endpoint是通过什么样的通讯手段暴露给客户端的,Contract —— 这个Endpoint对外暴露的是哪个Contract.
通过两个方法给ServiceHost添加Endpoint
1.3.1. ServiceHost.AddServiceEndpoint
AddServiceEndpoint方法有8种重载,ServiceHost提供了四种:
ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address);
ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address);
ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri);
ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri);
ServiceHost的父类ServiceHostBase也提供了四种:
ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address);
ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address);
ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri);
ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri);
其中参数implementedContract为Contract的完全名称,即名称空间。类名。
myServiceHost.AddServiceEndpoint(typeof(WCFService.IService), new BasicHttpBinding(), "");
1.3.2. ServiceHost.Description.Endpoints.Add(ServiceEndpoint item)
ServiceHost.Description 是一个 ServiceDescription 类型的对象。
ServiceDescription 是一个Service在内存中的一个完整的描述,包括服务的所有Endpoint,和每个Endpoint的各自的address、binding、contract和behaviors.
使用此方法先要根据Endpoint的ABC构造一个ServiceEndpoint 对象。
ServiceEndpoint(ContractDescription contract, Binding binding, EndpointAddress address)
其中ContractDescription这样通过ContractDescription的静态方法GetContract构造:
ContractDescription.GetContract(Type contractType);
ServiceEndpoint myServiceEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(WCFService.IService)), new BasicHttpBinding(), new EndpointAddress(baseAddress));
myServiceHost.Description.Endpoints.Add(myServiceEndpoint);
1.4. 视需要给ServiceHost添加behavior
ServiceHost. Behaviors是一个 IServiceBehavior类型的对象集合。
IserviceBehavior 提供了一个在整个服务范围内修改或则插入定制扩展的机制。
如果需要把服务通过WSDL对外暴露对服务的Metadata描述,就需要加一个ServiceMetadataBehavior类型的Behavior:
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = new Uri("http://localhost:8001/");
myServiceHost.Description.Behaviors.Add(behavior); // myServiceHost是ServiceHost实例
要发布Metadata,ServiceHost必须有一个http的baseAddress,所以在构造ServiceHost实例时,就需要在构造方法中加入这个http的baseAddress,这个http的baseAddress就是对外发布的Metadata地址。
1.5. 打开ServiceHost,开始提供服务
ServiceHost构建好了,添加了需要的Endpoint、behavior后,使用ServiceHost.Open()方法开发ServiceHost实例,开始对外提供服务。