WCF Fundamentals
Comparing ASP.NET Web Services to WCF Based on Development
Windows
Communication Foundation (WCF) has an ASP.NET compatibility mode option to
enable WCF applications to be programmed and configured like ASP.NET Web
services, and mimic their behavior. The following sections compare ASP.NET Web
services and WCF based on what is required to develop applications using both
technologies.ASP.NET Web services were developed for building applications that send and receive messages by using the Simple Object Access Protocol (SOAP) over HTTP. The structure of the messages can be defined using an XML Schema, and a tool is provided to facilitate serializing the messages to and from .NET Framework objects. The technology can automatically generate metadata to describe Web services in the Web Services Description Language (WSDL), and a second tool is provided for generating clients for Web services from the WSDL.
WCF is for enabling .NET Framework applications to exchange messages with other software entities. SOAP is used by default, but the messages can be in any format, and conveyed by using any transport protocol. The structure of the messages can be defined using an XML Schema, and there are various options for serializing the messages to and from .NET Framework objects. WCF can automatically generate metadata to describe applications built using the technology in WSDL, and it also provides a tool for generating clients for those applications from the WSDL.
1.6. Bindings
There is multiple aspects
of communication with any given service, and there are many possible
communication patterns: messages can be synchronous request/reply or
asynchronous fire-and-forget; messages can be bidirectional; messages can be
delivered immediately or queued; and the queues can be durable or volatile.
There are many possible transport protocols for the messages, such as HTTP
(or HTTPS), TCP, P2P (peer network), IPC (named pipes), or MSMQ. There are a
few possible message encoding options: you can chose plain text to enable
interoperability, binary encoding to optimize performance, or MTOM (Message
Transport Optimization Mechanism) for large payloads. There are a few options
for securing messages: you can choose not to secure them at all, to provide
transport-level security only, to provide message-level privacy and security,
and of course there are numerous ways for authenticating and authorizing the
clients. Message delivery might be unreliable or reliable end-to-end across
intermediaries and dropped connections, and the messages might be processed
in the order they were sent or in the order they were received. Your service
might need to interoperate with other services or clients that are only aware
of the basic web service protocol, or they may be capable of using the score
of WS-* modern protocols such as WS-Security and WS-Atomic Transactions. Your
service may need to interoperate with legacy clients over raw MSMQ messages,
or you may want to restrict your service to interoperate only with another
WCF service or client.
If you start counting all
the possible communication and interaction options, the number of
permutations is probably in the tens of thousands. Some of those choices may
be mutually exclusive, and some may mandate other choices. Clearly, both the
client and the service must be aligned on all these options in order to
communicate properly. Managing this level of complexity adds no business
value to most applications, and yet the productivity and quality implications
of making the wrong decisions are severe.
To simplify these choices
and make them more manageable, WCF groups together a set of such
communication aspects in bindings. A binding
is merely a consistent, canned set of choices regarding the transport
protocol, message encoding, communication pattern, reliability, security,
transaction propagation, and interoperability. Ideally, you would extract all
these "plumbing" aspects out of your service code and allow the
service to focus solely on the implementation of the business logic. Binding
enables you to use the same service logic over drastically different
plumbing.
You can use the
WCF-provided bindings as is, you can tweak their properties, or you can write
your own custom bindings from scratch. The service publishes its choice of
binding in its metadata, enabling clients to query for the type and specific
properties of the binding because the client must use the exact same binding
values as the service. A single service can support multiple bindings on
separate addresses.
1.6.1. The Standard Bindings
WCF defines nine standard
bindings:
Basic binding
Offered by the BasicHttpBinding class, this is designed to
expose a WCF service as a legacy ASMX web service, so that old clients can
work with new services. When used by the client, this binding enables new WCF
clients to work with old ASMX services.
TCP binding
Offered by the NetTcpBinding class, this uses TCP for
cross-machine communication on the intranet. It supports a variety of
features, including reliability, transactions, and security, and is optimized
for WCF-to-WCF communication. As a result, it requires both the client and
the service to use WCF.
Peer network binding
Offered by the NetPeerTcpBinding class, this uses peer
networking as a transport. The peer network-enabled client and services all subscribe
to the same grid and broadcast messages to it. Peer networking is beyond the
scope of this book since it requires an understanding of grid topology and
mesh computing strategies.
IPC binding
Offered by the NetNamedPipeBinding class, this uses named
pipes as a transport for same-machine communication. It is the most secure
binding since it cannot accept calls from outside the machine and it supports
a variety of features similar to the TCP binding.
Web Service (WS) binding
Offered by the WSHttpBinding class, this uses HTTP or
HTTPS for transport, and is designed to offer a variety of features such as
reliability, transactions, and security over the Internet.
Federated WS binding
Offered by the WSFederationHttpBinding class, this is a
specialization of the WS binding, offering support for federated security.
Federated security is beyond the scope of this book.
Duplex WS binding
Offered by the WSDualHttpBinding class, this is similar to
the WS binding except it also supports bidirectional communication from the
service to the client
MSMQ binding
Offered by the NetMsmqBinding class, this uses MSMQ for
transport and is designed to offer support for disconnected queued calls.
MSMQ integration binding
Offered by the MsmqIntegrationBinding class, this converts WCF
messages to and from MSMQ messages, and is designed to interoperate with
legacy MSMQ clients. Using this binding is beyond the scope of this book.
1.6.2. Format and Encoding
Each of the standard
bindings uses different transport and encoding, as listed in Table 1-1.
Having a text-based
encoding enables a WCF service (or client) to communicate over HTTP with any
other service (or client) regardless of its technology. Binary encoding over
TCP or IPC yields the best performance but at the expense of
interoperability, by mandating WCF-to-WCF communication.
1.6.3. Choosing a Binding
Choosing a binding for your
service should follow the decision-activity diagram shown in Figure 1-4.
Figure 1-4. Choosing a binding
The first question you
should ask yourself is whether your service needs to interact with non-WCF
clients. If the answer is yes, and if the client is a legacy MSMQ client,
choose the MsmqIntegrationBinding that enables your service
to interoperate over MSMQ with such a client. If you need to interoperate with
a non-WCF client and that client expects basic web service protocol (ASMX web
services), choose the BasicHttpBinding, which exposes your WCF service to the outside world as if it were
an ASMX web service (that is, a WSI-basic profile). The downside is that you
cannot take advantage of most of the modern WS-* protocols. However, if the
non-WCF client can understand these standards, choose one of the WS bindings,
such as WSHttpBinding, WSFederationBinding, or WSDualHttpBinding. If you can assume that
the client is a WCF client, yet it requires offline or disconnected
interaction, choose the NetMsmqBinding that uses MSMQ for transporting the messages. If the client requires
connected communication, but could be calling across machine boundaries,
choose the NetTcpBinding that communicates over
TCP. If the client is on the same machine as the service, choose the NetNamedPipeBinding that uses named pipes to
maximize performance. You may fine-tune binding selections based on
additional criteria such as the need for callbacks (WSDualHttpBinding) or federated security (WSFederationBinding).
1.6.4. Using a Binding
Each binding offers
literally dozens of configurable properties. There are three modes of working
with bindings. You can use the built-in bindings as is if they fit your
requirements. You can tweak and configure some of their properties such as
transaction propagation, reliability, and security. You can also write your
own custom bindings. The most common scenario is using an existing binding
almost as is, and merely configuring two or three of its aspects. Application
developers will hardly ever need to write a custom binding, but framework
developers may need to.
|
1.7. Endpoints
Every service is associated
with an address that defines where the service is, a binding that defines how
to communicate with the service, and a contract that defines what the service
does. This triumvirate governing the service is easy to remember as the ABC of the service. WCF formalizes this relationship
in the form of an endpoint. The endpoint is the
fusion of the address, contract, and binding (see Figure 1-5).
Figure 1-5. The endpoint
Every endpoint must have all
three elements, and the host exposes the endpoint. Logically, the endpoint is
the service's interface, and is analogous to a CLR or COM interface. Note in Figure 1-5 the use of the traditional
"lollipop" to denote an endpoint.
|
Every service must expose at
least one business endpoint and each endpoint has exactly one contract. All
endpoints on a service have unique addresses, and a single service can expose
multiple endpoints. These endpoints can use the same or different bindings and
can expose the same or different contracts. There is absolutely no relationship
between the various endpoints a service provides.
It is important to point out
that nothing in the service code pertains to its endpoints and they are always
external to the service code. You can configure endpoints either
administratively using a config file or programmatically.
1.7.1. Administrative Endpoint Configuration
Configuring an endpoint
administratively requires placing the endpoints in the hosting process' config
file. For example, given this service definition:
namespace MyNamespace
{
[ServiceContract]
interface IMyContract
{...}
class MyService : IMyContract
{...}
}
Example 1-6 shows the required entries in
the config file. Under each service type you list its endpoints.
Example 1-6. Administrative endpoint configuration
<system.serviceModel>
<services>
<service name = "MyNamespace.MyService">
<endpoint
address = "http://localhost:8000/MyService/"
binding = "wsHttpBinding"
contract = "MyNamespace.IMyContract"
/>
</service>
</services>
</system.serviceModel>
|
When you specify the service
and the contract type, you need to use fully qualified type names. I will omit
the namespace in the examples throughout the remainder of this book, but you
should use a namespace when applicable. Note that if the endpoint provides a
base address, then that address schema must be consistent with the binding,
such as HTTP with WSHttpBinding. A mismatch causes an exception at the service load time.
Example 1-7 shows a config file defining a
single service that exposes multiple endpoints. You can configure multiple
endpoints with the same base address as long as the URI is different.
Example 1-7. Multiple endpoints on the same service
<service name = "MyService">
<endpoint
address = "http://localhost:8000/MyService/"
binding = "wsHttpBinding"
contract = "IMyContract"
/>
<endpoint
address = "net.tcp://localhost:8001/MyService/"
binding = "netTcpBinding"
contract = "IMyContract"
/>
<endpoint
address = "net.tcp://localhost:8002/MyService/"
binding = "netTcpBinding"
contract = "IMyOtherContract"
/>
</service>
|
Administrative configuration
is the option of choice in the majority of cases because it provides the
flexibility to change the service address, binding, and even exposed contracts
without rebuilding and redeploying the service.
1.7.1.1. Using base addresses
In Example 1-7, each endpoint provided its
own base address. When you provide an explicit base address, it overrides any
base address the host may have provided.
You can also have multiple
endpoints use the same base address, as long as the endpoint addresses differ
in their URIs:
<service name = "MyService">
<endpoint
address = "net.tcp://localhost:8001/MyService/"
binding = "netTcpBinding"
contract = "IMyContract"
/>
<endpoint
address = "net.tcp://localhost:8001/MyOtherService/"
binding = "netTcpBinding"
contract = "IMyContract"
/>
</service>
Alternatively, if the host
provides a base address with a matching transport schema, you can leave the
address out, in which case the endpoint address will be the same as the base
address of the matching transport:
<endpoint
binding = "wsHttpBinding"
contract = "IMyContract"
/>
If the host does not provide
a matching base address, loading the service host will fail with an exception.
When you configure the
endpoint address you can add just the relative URI under the base address:
<endpoint
address = "SubAddress"
binding = "wsHttpBinding"
contract = "IMyContract"
/>
The endpoint address in this
case will be the matching base address plus the URI, and, again, the host must
provide a matching base address.
1.7.1.2. Binding configuration
You can use the config file
to customize the binding used by the endpoint. To that end, add the bindingConfiguration tag to the endpoint section, and name a
customized section in the bindings section of the config file. Example
1-8 demonstrates using this technique to enable transaction propagation.
What the transactionFlow tag does will be explained
in.
Example 1-8. Service-side binding configuration
<system.serviceModel>
<services>
<service name = "MyService">
<endpoint
address = "net.tcp://localhost:8000/MyService/"
bindingConfiguration = "TransactionalTCP"
binding = "netTcpBinding"
contract = "IMyContract"
/>
<endpoint
address = "net.tcp://localhost:8001/MyService/"
bindingConfiguration = "TransactionalTCP"
binding = "netTcpBinding"
contract = "IMyOtherContract"
/>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name = "TransactionalTCP"
transactionFlow = "true"
/>
</netTcpBinding>
</bindings>
</system.serviceModel>
|
As shown in Example 1-8, you can reuse the named
binding configuration in multiple endpoints simply by referring to it.
1.7.2. Programmatic Endpoint Configuration
Programmatic endpoint
configuration is equivalent to administrative configuration. Instead of
resorting to a config file, you rely on programmatic calls to add endpoints to
the ServiceHost instance. Again, these calls
are always outside the scope of the service code. ServiceHost provides overloaded versions
of the AddServiceEndpoint(
) method:
public class ServiceHost : ServiceHostBase
{
public ServiceEndpoint AddServiceEndpoint(Type implementedContract,
Binding binding,
string address);
//Additional members
}
You can provide AddServiceEndpoint( ) methods with either relative
or absolute addresses, just as with a config file. Example 1-9 demonstrates programmatic
configuration of the same endpoints as in Example
1-7.
Example 1-9. Service-side programmatic endpoint configuration
ServiceHost host = new ServiceHost(typeof(MyService));
Binding wsBinding = new WSHttpBinding( );
Binding tcpBinding = new NetTcpBinding( );
host.AddServiceEndpoint(typeof(IMyContract),wsBinding,
"http://localhost:8000/MyService");
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,
"net.tcp://localhost:8001/MyService");
host.AddServiceEndpoint(typeof(IMyOtherContract),tcpBinding,
"net.tcp://localhost:8002/MyService");
host.Open( );
|
When you add an endpoint
programmatically, the address is given as a string, the contract as a Type, and the binding as one of
the subclasses of the abstract class Binding, such as:
public class NetTcpBinding : Binding,...
{...}
To rely on the host base
address, provide an empty string if you want to use the base address, or just
the URI to use the base address plus the URI:
Uri tcpBaseAddress = new Uri("net.tcp://localhost:8000/");
ServiceHost host = new ServiceHost(typeof(MyService),tcpBaseAddress);
Binding tcpBinding = new NetTcpBinding( );
//Use base address as address
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,"");
//Add relative address
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,"MyService");
//Ignore base address
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,
"net.tcp://localhost:8001/MyService");
host.Open( );
As with administrative
configuration using a config file, the host must provide a matching base
address; otherwise, an exception occurs. In fact, there is no difference
between programmatic and administrative configuration. When you use a config
file, all WCF does is parse the file and execute the appropriate programmatic
calls in its place.
1.7.2.1. Binding configuration
You can programmatically set
the properties of the binding used. For example, here is the code required to
enable transaction propagation similar to Example
1-8:
ServiceHost host = new ServiceHost(typeof(MyService));
NetTcpBinding tcpBinding = new NetTcpBinding( );
tcpBinding.TransactionFlow = true;
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,
"net.tcp://localhost:8000/MyService");
host.Open( );
Note that when you're dealing
with specific binding properties, you typically interact with a concrete
binding subclass such as NetTcpBinding, and not its abstract base clas
Data Representation
The development of a Web service with ASP.NET
typically begins with defining any complex data types the service is to use.
ASP.NET relies on the XmlSerializer
to translate data represented by .NET Framework types to XML for transmission
to or from a service and to translate data received as XML into .NET Framework
objects. Defining the complex data types that an ASP.NET service is to use
requires the definition of .NET Framework classes that the XmlSerializer
can serialize to and from XML. Such classes can be written manually, or
generated from definitions of the types in XML Schema using the command-line
XML Schemas/Data Types Support Utility, xsd.exe.
The following is a list of key issues to know when
defining .NET Framework classes that the XmlSerializer can serialize to
and from XML:
- Only the public fields and properties of .NET Framework objects are translated into XML.
- Instances of collection classes can be serialized into XML only if the classes implement either the IEnumerable or ICollection interface.
- Classes that implement the IDictionary interface, such as Hashtable, cannot be serialized into XML.
- The great many attribute types in the System.Xml.Serialization namespace can be added to a .NET Framework class and its members to control how instances of the class are represented in XML.
WCF application development usually also begins with
the definition of complex types. WCF can be made to use the same .NET Framework
types as ASP.NET Web services.
The WCF DataContractAttribute
and DataMemberAttribute
can be added to .NET Framework types to indicate that instances of the type are
to be serialized into XML, and which particular fields or properties of the
type are to be serialized, as shown in the following sample code.
//Example One:
[DataContract]
public class LineItem
{
[DataMember]
public string ItemNumber;
[DataMember]
public decimal Quantity;
[DataMember]
public decimal UnitPrice;
}
//Example Two:
public class LineItem
{
[DataMember]
private string itemNumber;
[DataMember]
private decimal quantity;
[DataMember]
private decimal unitPrice;
public string ItemNumber
{
get
{
return this.itemNumber;
}
set
{
this.itemNumber = value;
}
}
public decimal Quantity
{
get
{
return this.quantity;
}
set
{
this.quantity = value;
}
}
public decimal UnitPrice
{
get
{
return this.unitPrice;
}
set
{
this.unitPrice = value;
}
}
}
//Example Three:
public class LineItem
{
private string itemNumber;
private decimal quantity;
private decimal unitPrice;
[DataMember]
public string ItemNumber
{
get
{
return this.itemNumber;
}
set
{
this.itemNumber = value;
}
}
[DataMember]
public decimal Quantity
{
get
{
return this.quantity;
}
set
{
this.quantity = value;
}
}
[DataMember]
public decimal UnitPrice
{
get
{
return this.unitPrice;
}
set
{
this.unitPrice = value;
}
}
}
The DataContractAttribute signifies that zero
or more of a type’s fields or properties are to be serialized, while the DataMemberAttribute
indicates that a particular field or property is to be serialized. The DataContractAttribute
can be applied to a class or structure. The DataMemberAttribute can be
applied to a field or a property, and the fields and properties to which the
attribute is applied can be either public or private. Instances of types that
have the DataContractAttribute applied to them are referred to as data
contracts in WCF. They are serialized into XML using DataContractSerializer.
The following is a list of the important differences
between using the DataContractSerializer and using the XmlSerializer
and the various attributes of the System.Xml.Serialization namespace.
- The XmlSerializer and the attributes of the System.Xml.Serialization namespace are designed to allow you to map .NET Framework types to any valid type defined in XML Schema, and so they provide for very precise control over how a type is represented in XML. The DataContractSerializer, DataContractAttribute and DataMemberAttribute provide very little control over how a type is represented in XML. You can only specify the namespaces and names used to represent the type and its fields or properties in the XML, and the sequence in which the fields and properties appear in the XML:
[DataContract(
Namespace="urn:Contoso:2006:January:29",
Name="LineItem")]
public class LineItem
{
[DataMember(Name="ItemNumber",IsRequired=true,Order=0)]
public string itemNumber;
[DataMember(Name="Quantity",IsRequired=false,Order = 1)]
public decimal quantity;
[DataMember(Name="Price",IsRequired=false,Order = 2)]
public decimal unitPrice;
}
Everything
else about the structure of the XML used to represent the .NET type is
determined by the DataContractSerializer.
- By not permitting much control over how a type is to be represented in XML, the serialization process becomes highly predictable for the DataContractSerializer, and, thereby, easier to optimize. A practical benefit of the design of the DataContractSerializer is better performance, approximately ten percent better performance.
- The attributes for use with the XmlSerializer do not indicate which fields or properties of the type are serialized into XML, whereas the DataMemberAttribute for use with the DataContractSerializer shows explicitly which fields or properties are serialized. Therefore, data contracts are explicit contracts about the structure of the data that an application is to send and receive.
- The XmlSerializer can only translate the public members of a .NET object into XML, the DataContractSerializer can translate the members of objects into XML regardless of the access modifiers of those members.
- As a consequence of being able to serialize the non-public members of types into XML, the DataContractSerializer has fewer restrictions on the variety of .NET types that it can serialize into XML. In particular, it can translate into XML types like Hashtable that implement the IDictionary interface. The DataContractSerializer is much more likely to be able to serialize the instances of any pre-existing .NET type into XML without having to either modify the definition of the type or develop a wrapper for it.
- Another consequence of the DataContractSerializer being able to access the non-public members of a type is that it requires full trust, whereas the XmlSerializer does not. The Full Trust code access permission give complete access to all resources on a machine that can be access using the credentials under which the code is executing. This options should be used with care as fully trusted code accesses all resources on your machine.
- The DataContractSerializer incorporates some support for versioning:
- The DataMemberAttribute has an IsRequired property that can be assigned a value of false for members that are added to new versions of a data contract that were not present in earlier versions, thereby allowing applications with the newer version of the contract to be able to process earlier versions.
- By having a data contract implement the IExtensibleDataObject interface, one can allow the DataContractSerializer to pass members defined in newer versions of a data contract through applications with earlier versions of the contract.
Despite all of the differences, the XML into which
the XmlSerializer serializes a type by default is semantically identical
to the XML into which the DataContractSerializer serializes a type,
provided the namespace for the XML is explicitly defined. The following class,
which has attributes for use with both of the serializers, are translated into
semantically identical XML by the XmlSerializer and by the DataContractAttribute:
[Serializable]
[XmlRoot(Namespace="urn:Contoso:2006:January:29")]
[DataContract(Namespace="urn:Contoso:2006:January:29")]
public class LineItem
{
[DataMember]
public string ItemNumber;
[DataMember]
public decimal Quantity;
[DataMember]
public decimal UnitPrice;
}
The binding specifies the set of protocols for
communicating with the application. The following table lists the
system-provided bindings that represent common options.
Name
|
Purpose
|
BasicHttpBinding |
Interoperability
with Web services and clients supporting the WS-BasicProfile 1.1 and Basic
Security Profile 1.0. |
WSHttpBinding |
Interoperability
with Web services and clients that support the WS-* protocols over HTTP. |
WSDualHttpBinding |
Duplex HTTP
communication, by which the receiver of an initial message does not reply
directly to the initial sender, but may transmit any number of responses over
a period of time by using HTTP in conformity with WS-* protocols. |
WSFederationBinding |
HTTP
communication, in which access to the resources of a service can be
controlled based on credentials issued by an explicitly-identified credential
provider. |
NetTcpBinding |
Secure, reliable,
high-performance communication between WCF software entities across a
network. |
NetNamedPipeBinding |
Secure, reliable,
high-performance communication between WCF software entities on the same
machine. |
NetMsmqBinding |
Communication
between WCF software entities by using MSMQ. |
MsmqIntegrationBinding |
Communication
between a WCF software entity and another software entity by using MSMQ. |
NetPeerTcpBinding |
Communication
between WCF software entities by using Windows Peer-to-Peer Networking. |
The system-provided binding, BasicHttpBinding,
incorporates the set of protocols supported by ASP.NET Web services.
Custom bindings for WCF applications are easily
defined as collections of the binding element classes that WCF uses to
implement individual protocols. New binding elements can be written to
represent additional protocols.
The internal behavior of service types can be
adjusted using the properties of a family of classes called behaviors. Here,
the ServiceBehaviorAttribute
class is used to specify that the service type is to be multithreaded.
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple]
public class DerivativesCalculatorServiceType: IDerivativesCalculator
Some behaviors, like ServiceBehaviorAttribute,
are attributes. Others, the ones with properties that administrators would want
to set, can be modified in the configuration of an application.
In programming service types, frequent use is made
of the OperationContext
class. Its static Current
property provides access to information about the context in which an operation
is running. OperationContext is similar to both the HttpContext
and ContextUtil
classes.
Hosting
ASP.NET Web services are compiled into a class
library assembly. A file called the service file is provided that has the
extension .asmx and contains an @ WebService directive that identifies the class that contains
the code for the service and the assembly in which it is located.
<%@ WebService Language="C#" Class="Service,ServiceAssembly" %>
The service file is copied into an ASP.NET
application root in Internet Information Services (IIS), and the assembly is
copied into the \bin subdirectory of that application root. The application is
then accessible by using the uniform resource locator (URL) of the service file
in the application root.
WCF services can readily be hosted within IIS 5.1 or
6.0, the Windows Process Activation Service (WAS) that is provided as part of
IIS 7.0, and within any .NET application. To host a service in IIS 5.1 or 6.0,
the service must use HTTP as the communications transport protocol.
To host a service within IIS 5.1, 6.0 or within WAS,
use the follows steps:
1.
Compile the service type into a class library assembly.
2.
Create a service file with a .svc extension with an @ ServiceHost directive to identify the
service type:
<%@ServiceHost language=”c#” Service="MyService" %>
3.
Copy the service file into a virtual directory, and the assembly into
the \bin subdirectory of that virtual directory.
4.
Copy the configuration file into the virtual directory, and name it
Web.config.
The application is then accessible by using the URL
of the service file in the application root.
To host a WCF service within a .NET application,
compile the service type into a class library assembly referenced by the
application, and program the application to host the service using the ServiceHost
class. The following is an example of the basic programming required:
string httpBaseAddress = "http://www.contoso.com:8000/";
string tcpBaseAddress = "net.tcp://www.contoso.com:8080/";
Uri httpBaseAddressUri = new Uri(httpBaseAddress);
Uri tcpBaseAddressUri = new Uri(tcpBaseAddress);
Uri[] baseAdresses = new Uri[] {
httpBaseAddressUri,
tcpBaseAddressUri};
using(ServiceHost host = new ServiceHost(
typeof(Service), //”Service” is the name of the service type baseAdresses))
{
host.Open();
[…] //Wait to receive messages
host.Close();
}
This example shows how addresses for one or more
transport protocols are specified in the construction of a ServiceHost.
These addresses are referred to as base addresses.
The address provided for any endpoint of a WCF
service is an address relative to a base address of the endpoint’s host. The
host can have one base address for each communication transport protocol. The
base address of an endpoint is relative to whichever of the base addresses of
the host is the base address for the communication transport protocol of the
endpoint. In the sample configuration in the preceding configuration file, the BasicHttpBinding
selected for the endpoint uses HTTP as the transport protocol, so the address
of the endpoint, EchoService, is relative to the host’s HTTP base address. In the case of the host
in the preceding example, the HTTP base address is
http://www.contoso.com:8000/. For a service hosted within IIS or WAS, the base
address is the URL of the service’s service file.
Only services hosted in IIS or WAS, and which are
configured with HTTP as the transport protocol exclusively, can be made to use
WCF ASP.NET compatibility mode option. Turning that option on requires the
following steps.
1.
The programmer must add the AspNetCompatibilityRequirementsAttribute
attribute to the service type and specify that ASP.NET compatibility mode is
either allowed or required.
[System.ServiceModel.Activation.AspNetCompatibilityRequirements(
RequirementsMode=AspNetCompatbilityRequirementsMode.Require)]
public class DerivativesCalculatorServiceType: IDerivativesCalculator
2.
The administrator must configure the application to use the ASP.NET
compatibility mode.
<configuration>
<system.serviceModel>
<services>
[…]
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
</configuration>
WCF
applications can also be configured to use .asmx as the extension for their
service files rather than .svc.
<system.web>
<compilation>
<compilation debug="true">
<buildProviders>
<remove extension=".asmx"/>
<add extension=".asmx"
type="System.ServiceModel.ServiceBuildProvider,
Systemm.ServiceModel,
Version=3.0.0.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
</buildProviders>
</compilation>
</compilation>
</system.web>
That
option can save you from having to modify clients that are configured to use
the URLs of .asmx service files when modifying a service to use WCF.
Client Development
Clients for ASP.NET Web services are generated using
the command-line tool, WSDL.exe, which provides the URL of the .asmx file as
input. The corresponding tool provided by WCF is Service Model
Metadata Tool (Svcutil.exe). It generates a code module with the definition
of the service contract and the definition of a WCF client class. It also
generates a configuration file with the address and binding of the service.
In programming a client of a remote service it is
generally advisable to program according to an asynchronous pattern. The code
generated by the WSDL.exe tool always provides for both a synchronous and an
asynchronous pattern by default. The code generated by the Service Model
Metadata Tool (svcutil.exe) can provide for either pattern. It provides for
the synchronous pattern by default. If the tool is executed with the /async
switch, then the generated code provides for the asynchronous pattern.
There is no guarantee that names in the WCF client
classes generated by ASP.NET’s WSDL.exe tool, by default, match the names in
WCF client classes generated by the Svcutil.exe tool. In particular, the names
of the properties of classes that have to be serialized using the XmlSerializer
are, by default, given the suffix Property in the code generated by the
Svcutil.exe tool, which is not the case with the WSDL.exe tool.
Message Representation
The headers of the SOAP messages sent and received
by ASP.NET Web services can be customized. A class is derived from SoapHeader
to define the structure of the header, and then the SoapHeaderAttribute
is used to indicate the presence of the header.
public class SomeProtocol : SoapHeader
{
public long CurrentValue;
public long Total;
}
[WebService]
public interface IEcho
{
SomeProtocol ProtocolHeader
{
get;
set;
}
[WebMethod]
[SoapHeader("ProtocolHeader")]
string PlaceOrders(PurchaseOrderType order);
}
public class Service: WebService, IEcho
{
private SomeProtocol protocolHeader;
public SomeProtocol ProtocolHeader
{
get
{
return this.protocolHeader;
}
set
{
this.protocolHeader = value;
}
}
string PlaceOrders(PurchaseOrderType order)
{
long currentValue = this.protocolHeader.CurrentValue;
}
}
The WCF provides the attributes, MessageContractAttribute,
MessageHeaderAttribute,
and MessageBodyMemberAttribute
to describe the structure of the SOAP messages sent and received by a service.
[DataContract]
public class SomeProtocol
{
[DataMember]
public long CurrentValue;
[DataMember]
public long Total;
}
[DataContract]
public class Item
{
[DataMember]
public string ItemNumber;
[DataMember]
public decimal Quantity;
[DataMember]
public decimal UnitPrice;
}
[MessageContract]
public class ItemMesage
{
[MessageHeader]
public SomeProtocol ProtocolHeader;
[MessageBody]
public Item Content;
}
[ServiceContract]
public interface IItemService
{
[OperationContract]
public void DeliverItem(ItemMessage itemMessage);
}
This syntax yields an explicit representation of the
structure of the messages, whereas the structure of messages is implied by the
code of an ASP.NET Web service. Also, in the ASP.NET syntax, message headers
are represented as properties of the service, such as the ProtocolHeader property in the previous
example, whereas in WCF syntax, they are more accurately represented as
properties of messages. Also, WCF allows message headers to be added to the
configuration of endpoints.
<service name="Service ">
<endpoint
address="EchoService"
binding="basicHttpBinding"
contract="IEchoService ">
<headers>
<dsig:X509Certificate
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
...
</dsig:X509Certificate>
</headers>
</endpoint>
</service>
That option allows you to avoid any reference to
infrastructural protocol headers in the code for a client or service: the
headers are added to messages because of how the endpoint is configured.
No comments:
Post a Comment