Learning WCF Chapter1 Creating a New Service from Scratch
You’re about to be introduced to the WCF service.
This lab isn’t your typical “Hello World”—it’s “Hello Indigo”!
In this lab,you will learn how to build a new WCF service and in the process learn the minimum requirements of service development and consumption.
Here’s a short list of things you’ll accomplish:
• Create a new service contract and service implementation
• Programmatically configure a service host, its endpoints, and bindings
• Create a client application and open a client channel proxy to invoke the service
Now,before you start thinking “been there,done that,” this simple lab will be slightly different
because I’m going to give you some practical design tips that ensure configurability and appropriate decoupling of service,host,and client.
In addition,I’ll be diving deeper into basic concepts such as services,service contracts,endpoints, bindings, ServiceHost, and channels.
Lab: Creating Clients and Services Programmatically
In this first lab,you will create a new solution with three projects: a service,a host,and a client.
When you run the service host,you’ll expose a single service endpoint.
The client application will access service operations through that endpoint.
You’ll host the service in a console application and invoke the service using a manually constructed proxy.
This lab will teach you the basic requirements for creating,hosting,and consuming a service with WCF.
Creating a new service
The first thing you will do is create a new service contract with a single operation and implement this contract on a new service type.
1. In this lab,everything begins from scratch,so you’ll start by creating a new Visual Studio solution.
Open a new instance of Visual Studio 2008.
Select File ➝New ➝ Project,and from the New Project dialog,create a new Blank Solution in the <YourLearningWCFPath>\Labs\Chapter1 directory.
Name the solution ServiceFromScratch. Verify that the .NET Framework version selected in the dropdown list is set to .NET Framework 3.0 or .NET Framework 3.5.
Click OK to create the empty solution.
2. Create the service project.
From Solution Explorer,right-click on the solution node and select Add ➝ New Project.
Select the Class Library template,name the project HelloIndigo,and make sure the location path matches the solution at <YourLearningWCFPath>\Labs\Chapter1\ServiceFromScratch.
Click OK to create the new project.
3. Now you will create your first service contract.
From Solution Explorer,rename the project’s only class file to Service.cs.
Open this file in the code window.
Add a new interface named IHelloIndigoService in Service.cs.
Add a single method to the interface, HelloIndigo, with the signature shown here:
public interface IHelloIndigoService
{
string HelloIndigo( );
}
4. Add a reference to the System.ServiceModel assembly. From Solution Explorer,right-click References and select System.ServiceModel from the list.
You’ll also need to add the following using statement to Service.cs:
using System.ServiceModel;
5. To turn this interface into a service contract,you’ll need to explicitly decorate the interface with the ServiceContractAttribute.
In addition,each method should be decorated with the OperationContractAttribute to include it in the service contract.
In this case,you’ll make IHelloIndigoService a service contract and expose HelloIndigo( ) as its only service operation by applying these attributes as shown here:
[ServiceContract(Namespace="http://www.thatindigogirl.com/samples/2006/06")]
public interface IHelloIndigoService
{
[OperationContract]
string HelloIndigo( );
}
Note:Providing a namespace for the ServiceContractAttribute reduces the possibility of naming collisions with other services. This will be discussed in greater detail in Chapter 2.
6. In the same file,create a service type to implement the service contract.
You can modify the existing class definition,renaming it to HelloIndigoService.
Then add the IHelloIndigoService interface to the derivation list and implement HelloIndigo( ) with the following code:
public class HelloIndigoService : IHelloIndigoService
{
public string HelloIndigo( )
{
return "Hello Indigo";
}
}
7. Compile the service project.
At this point,you’ve created a service contract with a single operation and implemented it on a service type.
The service is complete at this point,but to consume it from a client application, you will need to host it first.
Hosting a service
Next,add a new console application to the solution.
This will be the host application.
You’ll instantiate a ServiceHost instance for the service type and configure a single endpoint.
1. Go to the Solution Explorer and add a new Console Application project to the solution.
Name the new project Host.
2. Add a reference to the System.ServiceModel assembly,and add the following using statement to Program.cs:
using System.ServiceModel;
3. You will be writing code to host the HelloIndigoService type.
Before you can do this, you must add a reference to the HelloIndigo project.
4. Create a ServiceHost instance and endpoint for the service.
Open Program.cs in the code window and modify the Main( ) entry point,adding the code shown in Example 1-1.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel; namespace Host
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService), new Uri("http://localhost:8000/HelloIndigo")))
{
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService), new BasicHttpBinding(), "HelloIndigoService");
host.Open();
Console.WriteLine("Press <ENTER> to terminate the service host");
Console.ReadLine();
}
}
}
}
Example 1-1. Code to programmatically initialize the ServiceHost
This code initializes a ServiceHost instance specifying the service type and a base address where relative service endpoints can be located.
It also adds a single relative endpoint for the service.
In this case,a base address is provided for HTTP protocol,and the relative endpoint uses one of the standard bindings, BasicHttpBinding, based on HTTP protocol.
5. Compile and run the host to verify that it works.
From Solution Explorer,rightclick on the Host project node and select “Set as Startup Project.”
Run the project (F5),and you should see console output similar to that shown in Figure 1-17.
Figure 1-17. Console output for the host application
6. Stop debugging and return to Visual Studio.
You now have a host application for the service.
When it is running clients will be able to communicate with the service.
The next step is to create a client application.
Creating a proxy to invoke a service
Now you will create a new console application to test the service.
To do this,the client requires metadata from the service and information about its endpoint.
This information will be used to initialize a client proxy that can invoke service operations.
1. Go to Solution Explorer and add a new Console Application to the solution.
Name the new project Client.
2. As you might expect,this project also requires a reference to System.ServiceModel.
Add this reference and add the following using statement to Program.cs:
using System.ServiceModel;
3. Copy the service contract to the client.
First,add a new class to the Client project,naming the file ServiceProxy.cs.
Open this new file in the code window and add the IHelloIndigoService contract metadata as shown in Example 1-2.
Example 1-2. Service contract metadata for the client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel; namespace Client
{
[ServiceContract(Namespace = "http://www.thatindigogirl.com/samples/2006/06")]
public interface IHelloIndigoService
{
[OperationContract]
string HelloIndigo();
}
}
This service contract supplies the necessary metadata to the client,describing namespaces and service operation signatures.
4. Now you can add code to invoke the service endpoint.
Open Program.cs and modify the Main( ) entry point by adding the code as shown in Example 1-3.
Example 1-3. Code to invoke a service through its generated proxy
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel; namespace Client
{
class Program
{
static void Main(string[] args)
{
EndpointAddress ep = new EndpointAddress("http://localhost:8000/HelloIndigo/HelloIndigoService");
IHelloIndigoService proxy = ChannelFactory<IHelloIndigoService>.CreateChannel(new BasicHttpBinding(), ep);
string s = proxy.HelloIndigo();
Console.WriteLine(s);
Console.WriteLine("Press <ENTER> to terminate Client.");
Console.ReadLine();
}
}
}
This code uses the ChannelFactory to create a new channel to invoke the service.
This strongly typed channel reference acts as a proxy.
The code also initializes an EndpointAddress with the correct address and binding expected by the service endpoint.
5. Test the client and service.
Compile the solution and run the Host project first,followed by the Client project.
The Client console output should look similar to that shown in Figure 1-18.
In the next few sections,I will explain in more detail the steps you completed and
the features you explored in the lab.
Assembly Allocation
The first thing I’d like to touch on is the allocation of assemblies when you create a new solution that includes services.
For example,in this lab you created a new solution with three projects: one for the service,another for the host,and another for the client.
Note that the service definition is decoupled from the host project.
This is an approach I always recommend because it allows you to host the same service in multiple environments.
For example,you may need to expose a service behind the firewall over TCP,and yet also allow remote,interoperable clients to consume it over HTTP.
These two approaches require distinct hosting environments (specifically,a Windows service and IIS,as I will discuss in Chapter 4 at length).
For simplicity,many examples may couple service and host,but this is merely a convenience—not a practical approach.
As such,at a minimum I recommend that you always create a separate project for service contracts and services.
Services are the window through which business functionality is invoked,but business logic has no place in the service assembly.
Business functionality should never be coupled with the service implementation because it is possible that multiple services and applications may need to reuse the same business logic.
Furthermore,while you may use services to reach that functionality in most cases,what if you needed to expose an Enterprise Service component to interoperate with a particular application or system?
If business logic is stored in its own assemblies,this type of sharing is made easy.
Another reason to decouple business logic from service implementation is to improve manageability and versioning.
The service tier may need to coordinate logging activities and exception handling around calls to business components,
and the service tier may need to be versioned,while business components and associated functionality have not changed.
For this reason,I always recommend that business components,data access components,and other dependencies of the business tier also represent a separate set of assemblies in your solution.
Figure 1-19 illustrates this breakdown from a high level.
Figure 1-19 illustrates this breakdown from a high level.
There may be times when it is desirable to share the service contracts with client applications.
In that case,service contracts and service implementations may also be decoupled.
This makes it possible to share the metadata of the service without sharing the implementation.
Note:Later in this chapter,you’ll see a scenario in which the service contract and service are decoupled.
Defining a Service
The first step in creating a service is to define a service contract.
You create a service contract by applying the ServiceContractAttribute to an interface or type.
Methods on the interface or type will not be included in the service contract until the OperationContractAttribute is applied.
In a typical service contract,all methods will be included in the contract—after all,the entire reason for defining a service contract is to expose operations as part of a service.
Business interfaces should not be directly converted into service contracts.
Likewise,business components should not be directly converted to services.
The service tier should instead be explicitly defined with the sole purpose of exposing public functionality and should internally consume business components,
rather than embed business logic with the service implementation.
When you implement a service contract as an interface,the service type implements this interface.
In this lab,the service implements a single service contract,IHelloIndigoService.
This contract exposes a single operation, HelloIndigo( ).
An alternative to this approach is to apply both the ServiceContractAttribute and the OperationContractAttribute directly to the service type.
Example 1-4 shows the changes you would make to the lab to achieve this.
Here is a summary of those changes:
When you apply the ServiceContractAttribute to the service type,the service type name becomes the official name of the service contract.
Thus,this is the name that must be provided when you create a new endpoint (see AddServiceEndpoint( )).
On the client side,the service contract can still be represented as an interface (the client only requires metadata) but the name of that interface (the service contract)must match the new service contract name,
HelloIndigoService—instead of IHelloIndigoService.
To update the lab,you can rename the interface at the client,or specify a value for the Name property of the ServiceContactAttribute as shown in Example 1-4.
Service contracts are discussed in Chapter 2.
Note:The following sample illustrates the coupling of service contracts with service type: <YourLearningWCFPath>\Sample\ServiceContracts\ServiceContractOnServiceType.
Example 1-4. Changes that support defining the service contract with the service type
// HelloIndigo Project – Service.cs //取消接口,直接在服务类型上加ServiceContract和在方法加OperationContract
[ServiceContract(Namespace = "http://www.thatindigogirl.com/samples/2006/06")]
public class HelloIndigoService
{
[OperationContract]
public string HelloIndigo( )
{
return "Hello Indigo";
}
}
// Host Project – Program.cs //接口不存在了,需要使用类型
host.AddServiceEndpoint(typeof(HelloIndigo.HelloIndigoService), new BasicHttpBinding( ),
"HelloIndigoService");
// Client Project – ServiceProxy.cs //需要显示指定服务类型
[ServiceContract(Name="HelloIndigoService", Namespace = "http://www.thatindigogirl.com/
samples/2006/06")]
public interface IHelloIndigoService
{
[OperationContract]
string HelloIndigo( );
}
Hosting a Service
Any managed process can host services. Within that process,you can create one or more ServiceHost instances,each associated with a particular service type and exposing one or more endpoints for that type.
This lab shows you how to host a service by creating an instance of the ServiceHost type for the HelloIndigoService type within a console application.
Before opening the ServiceHost instance,you can also provide it with base addresses if you are planning to create relative endpoints.
In order to reach the service,at least one endpoint is required.
To programmatically supply base addresses to the ServiceHost,you can pass them to the constructor.
The ServiceHost type also provides an AddServiceEndpoint( ) method to create endpoints as shown here (from Example 1-1):
using (ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService),new Uri("http://localhost:8000/HelloIndigo")))
{
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),new BasicHttpBinding( ), "HelloIndigoService");
// other code
}
In a simple scenario,the ServiceHost need only know its service type and associated endpoints where the service can be reached.
This information is used to create a server channel that can receive and process messages.
The channel is created when you call the Open( ) method on the ServiceHost instance.
This creates a channel listener to receive messages for the service through its associated endpoints.
The receiving channel processes incoming messages,invokes service operations,and processes responses.
When the Close( ) method is called,the channel is gracefully disposed of after processing any remaining requests.
In Example 1-1, Close( ) is automatically called when code block associated with the using statement ends.
Note:The using statement can be applied to any type that implements IDisposable.
At the end of the statement, Dispose( ) is called within a try…finally block to ensure cleanup even in the case of an exception.
Dispose() calls Close()—but if the channel has faulted (faults are discussed in Chapter 8) Abort() should be called instead of Close().
Thus,the using statement is convenient for simple code samples, but not a general best practice.
Exposing Service Endpoints
Endpoints expose service functionality at a particular address.
Each endpoint is associated with a particular contract and a set of protocols as defined by the binding configuration.
For each service,one or more endpoints may be exposed if multiple contracts are present or if multiple protocols are desired to access service functionality.
Figure 1-20 illustrates how the ServiceHost instance exposes endpoints to clients and how the proxy invokes service operations at a particular endpoint.
Figure 1-20. ServiceHost exposes endpoints and client proxies target a specific endpoint
As the lab illustrates,to create a service endpoint you provide an address,a binding,and a contract.
Addresses
The address can be a complete URI or a relative address like that used in the lab.
The following shows you how to initialize an endpoint with a complete URI without supplying a base address to the ServiceHost:
using (ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService)))
{
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),new BasicHttpBinding( ), "http://localhost:8000/HelloIndigo/HelloIndigoService");
// other code
}
If you supply a relative address it is concatenated with the ServiceHost base address for the matching protocol.
The following illustrates providing an HTTP base address to the ServiceHost constructor and providing a relative address to AddServiceEndpoint( ):
using (ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService),new Uri("http://localhost:8000/HelloIndigo")))
{
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),new BasicHttpBinding( ), "HelloIndigoService");
// other code
}
// Resulting endpoint address
http://localhost:8000/HelloIndigo/HelloIndigoService
In practice,a base address should be supplied for each transport protocol over which the service can be accessed—for example,HTTP,TCP,named pipes,or MSMQ.
In the event an endpoint address includes a complete URI,the base address will be ignored.
Note:Using relative endpoint addressing makes it possible to modify the base URI to move all associated relative endpoints to a new domain or port.
This can simplify the deployment process.
Bindings
The binding provided to an endpoint can be any of the standard bindings supplied by the service model.
In this example,a new instance of the standard BasicHttpBinding is used to initialize the endpoint:
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),new BasicHttpBinding( ), "HelloIndigoService");
The choice of binding defines the communication channel.
For an endpoint,BasicHttpBinding,for example,supports requests over HTTP protocol sent in text format without any additional protocols for addressing,reliable messaging,security,or transactions.
Note:In this chapter,you will employ other standard bindings,but you
should look to Chapter 3 for an in-depth discussion of bindings,channels, and overall service model architecture.
Contracts
Each endpoint is associated with a particular service contract that determines the operations available at the endpoint.
Only one service contract exists in this lab,but a service with multiple contracts could expose a different endpoint for each contract it wants to make accessible to clients.
Creating a Client Proxy
Clients use a proxy to consume a service endpoint.
A proxy can be created manually using a channel factory,or it can be generated using tools.
This lab explores the former and shows you the bare necessities required to communicate with a service:
• The address of the service endpoint
• The protocols required to communicate with the service endpoint, or the binding
• The service contract metadata as described by the service contract associated with the endpoint
Essentially,the client proxy requires information about the service endpoint it wishes to consume.
In this lab,you learned how to manually create the proxy using ChannelFactory<T>, as shown here:
EndpointAddress ep = new EndpointAddress("http://localhost:8000/HelloIndigo/HelloIndigoService");
IHelloIndigoService proxy = ChannelFactory<IHelloIndigoService>.CreateChannel(new BasicHttpBinding( ), ep);
ChannelFactory<T> is a service model type that can generate the client proxy and underlying channel stack.
You provide the address,binding,and service contract type and call CreateChannel( ) to generate the channel stack discussed earlier.
In this lab,you made a copy of the service contract (not the implementation) in the client application in order to supply it as the generic parameter type to ChannelFactory<T>.
The address and binding supplied matched those of the service.
The result is that the client proxy knows where to send messages,what protocols to use,and which operations it can call.
In order for communication between client and service to succeed,the binding must be equivalent to the binding specified for the service endpoint.
Equivalence means that the transport protocol is the same,the message-encoding format is the same,and any additional messaging protocols used at the service to serialize messages are also used at the client.
This lab achieves this by applying the same standard binding,BasicHttpBinding,at the client and service—thus,they are equivalent.
Another requirement for successful communication is that the service contract used to initialize the proxy has equivalent operation signatures and namespace definitions.
This is achieved in this lab by making an exact copy of the service contract at the client.
Note: You may be wondering: how can the client discover the correct address,binding,and contract associated with a service endpoint?
In the next lab,you’ll learn how to generate client proxies and configuration to consume a service without having access to the service code base.
Learning WCF Chapter1 Creating a New Service from Scratch的更多相关文章
- Learning WCF Chapter1 Hosting a Service in IIS
How messages reach a service endpoint is a matter of protocols and hosting. IIS can host services ov ...
- Learning WCF Chapter1 Generating a Service and Client Proxy
In the previous lab,you created a service and client from scratch without leveraging the tools avail ...
- Learning WCF Chapter1 Exposing Multiple Service Endpoints
So far in this chapter,I have shown you different ways to create services,how to expose a service en ...
- Learning WCF Chapter1 Summary
SummaryThis chapter covered a lot of ground,beginning with a look at the purpose of WCF,the problems ...
- Learning WCF:Life Cycle of Service instance
示例代码下载地址:WCFDemo1Day 概述 客户端向WCF服务发出请求后,服务端会实例化一个Service对象(实现了契约接口的对象)用来处理请求,实例化Service对象以及维护其生命周期的方式 ...
- Learning WCF 书中的代码示例下载地址
Learning WCF Download Example Code 第一个压缩文件LearningWCF.zip是VS2005创建的项目,不要下载这个. 建议下载VS2008版的,以及Media
- 【转】WCF光芒下的Web Service
WCF光芒下的Web Service 学习.NET的开发人员,在WCF的光芒照耀下,Web Service 似乎快要被人遗忘了.因为身边做技术的人一开口就是WCF多么的牛逼!废话不多,本人很久不写博客 ...
- Learning WCF Chapter2 Service Contracts
A service contract describes the operations supported by a service,the message exchange pattern they ...
- Learning WCF Chapter2 Service Description
While messaging protocols are responsible for message serialization formats,there must be a way to c ...
随机推荐
- TMemIniFile 与TIniFile 区别
在uses 申明 Inifiles MyStream:TMemIniFile; MyStream:=TMemIniFile.Create('c:\proxy.ini'); memo1.Text:=My ...
- 如何恢复oracle中已删除的表
在9i中Oracle引入了flashback的概念,可以将数据返回到某个时间点,但对于诸如drop/truncate等DDL语句却尚不支持.进入Oracle10g,这一缺陷得到了弥补.可以将丢失掉的表 ...
- iOS9的那些坑 — — WeiboSDK registerApp启动就崩溃
在升级Xcode7.2.1后,在App加载时直接崩掉,仔细看了,发现是在在注册微博SDK的时候报错: [WeiboSDK registerApp:WBAPPKey]; 查了很多资料,最后在github ...
- Oracle存储过程的理解
在大专时候学的专业是数据库管理专业,在学校学了各种各样的数据 MSSQL.ORACLE.MySQL. 那时候学数据大部分只学到了些皮毛,仅仅只会按照书上SQL语句,输入计算机得出结果,就很有成就感. ...
- angularJS的核心特性
前几天师傅让我了解一下angularJS,angularJS是一个前端框架,具体的优缺点和运用场景我现在也还没有搞清楚,暂时就先不做描述了,留到运用以后进行补充吧. angularJS四大核心特性:M ...
- io流之写文件
用Java程序写文件有多种方式,对于不同类型的数据,有不同的写方法.写文件的关键技术点如下: 1.FileOutputStream打开文件输出流,通过write方法以字节为单位写文件,是写文件最通用的 ...
- LA 3708 Graveyard(推理 参考系 中位数)
Graveyard Programming contests became so popular in the year 2397 that the governor of New Earck -- ...
- ubuntu svn安装测试
本机环境 :ubuntu 12.4 LTS desktop 1 sudo apt-get install subversion #安装svn 2 sudo mkdir /home/lzj/s ...
- Docker命令使用详解
其中<>括起来的参数为必选, []括起来为可选 docker -exec -i -t 3f407013d8c0 /bin/bash 进入容器 docker version查看dock ...
- Ubuntu 之旅—— 调整扩展屏分辨率
打开终端输入 xrandr 得到如下信息 Screen 0: minimum 320 x 200, current 2390 x 768, maximum 8192 x 8192 LVDS conne ...