Вы находитесь на странице: 1из 12

Chapter 2.

Service Contracts

A service contract describes the operations a service can perform. A service must have at least one
service contract, and it can have more than one. You can think of a service contract as follows:
 It describes the client-callable operations (functions) your service provides.
 It maps the interface and methods of your service to a platform-independent description.

 It describes message exchange patterns that the service can have with another party. Some
service operations might be one-way; others might require a request-reply pattern.

 It is analogous to the <portType>  element in WSDL.

 It It is analogous to the element in WSDL

1. A Service can have more than one service contract but it should have at least one Service
contract
2. can be define using [ServiceContract] and [OperationContract] attribute
3. [ServiceContract] attribute is similar to the [WebServcie] attribute in the WebService
4. and [OpeartionContract] is similar to the [WebMethod] in WebService.
5. [ServiceContract]
6. public interface IReservation
7. {
8. [OperationContract]
9. string StartReservation(string lastName, string firstName);
10. [OperationContract]
11. bool ReserveRoom(string arriveDate, string departDate, int guests);
12. [OperationContract]
13. string ConfirmReservation(bool authorize);
14. }

Once you define a service contract using an interface, you can write a class to implement the interface.
For example:
public class Reservation : IReservation
{
public string StartReservation(string lastName, string firstName)
{
...
}
public bool ReserveRoom(string arriveDate, string departDate, int guests)
{
...
}
public string ConfirmReservation(bool authorize)
{
...
}
}
If you don't like using interfaces, you can instead define the service contract directly against the
implementation class and skip the interface altogether. The following class both defines and
implements a service contract.
[ServiceContract]
public class Reservation
{
[OperationContract]
public string StartReservation(string lastName, string firstName)
{
...
}
[OperationContract]
public bool ReserveRoom(string arriveDate, string departDate, int guests)
{
...
}
[OperationContract]
public string ConfirmReservation(bool authorize)
{
...
}
}

A service contract can specify requirements that must be satisfied by endpoint bindings.
The [BindingRequirements]attribute specifies binding requirements for the contract. The following
service contract specifies a requirement for ordered delivery of messages.
[ServiceContract]
[BindingRequirements(RequireOrderedDelivery=true)]
public interface IReservation
{
[OperationContract]
string StartReservation(string lastName, string firstName);
[OperationContract]
bool ReserveRoom(string arriveDate, string departDate, int guests);
[OperationContract]
string ConfirmReservation(bool authorize);
}

The [ServiceContract], [BindingRequirements], and  [OperationContract]  attributes contain many


parameters for specifying service contract details. For detailed information on these attributes, see the
section titled "Programming Service Contracts" later in this chapter.

Three modes of communication between client and service are


 Request- Replay
 One-Way
 Callback
Programming Service Contracts
You define a service contract by annotating an interface or class with the [ServiceContract]  attribute.
You indicate service operations by using [OperationContract]  attributes on the methods of the
interface. You can also specify a[BindingRequirements]  attribute for the interface.
[ServiceContract]
public interface ISpellChecker
{
[OperationContract]
void OpenDocument(string documentText);
[OperationContract]
string[] CheckSpelling();
[OperationContract]
void MakeCorrection(int location, string replacementText);
[OperationContract]
string CloseDocument();
}

Service contracts can inherit from each other.


The [ServiceContract] Attribute
The [ServiceContract]  attribute marks an interface or class as a service contract.
[ServiceContract(Session=true, FormatMode=XmlSerializer)]
public interface IMyService
{
...
}

You can specify the following parameters in any order for the [ServiceContract]  attribute:
CallbackContract (type)

References a client callback contract used for duplex communication. The default is no
callback contract.

FormatMode (ContractFormatMode)

Selects a serializer. A serializer is a class used to perform serialization and deserialization. You
can set this property to XmlFormatter  or XmlSerializer. The default is XmlFormatter.

Name (string)

Specifies the name of the service contract. If this parameter is omitted, the default is the
interface name.

Namespace (string)

Specifies the namespace of the service contract. If this parameter is omitted, the default is the
interface namespace.

Session (boolean)

Indicates whether the contract requires a session. The default is false.

Style (ServiceOperationStyle)
Determines how the WSDL metadata for the service is formatted. Possible values
are DocumentWrapped,DocumentBare, and Rpc. The default is DocumentWrapped. This
property also exists at the [OperationContract]level.

Use (ServiceOperationBindingUse)

Determines whether the message format is literal or encoded. Possible values


are Literal  and Encoded. The default is Literal. This property also exists at
the [OperationContract]  level.

CallbackContract: Defining a Duplex Service Contract


When you use the duplex messaging pattern, both the service and the client implement a contract of
service operations. The client's contract is called the callback contract. Thus, for duplex messaging a
pair of contracts is always required.
To define a duplex contract pair, you define two sets of interfaces as service contracts and then link
them. The[ServiceContract]  attribute on the service contract should contain
a CallbackContract=typeof(type)  parameter that references the client callback contract.
[ServiceContract(CallbackContract=typeof(ISampleClientContract))]
public interface ISampleContract
{
[OperationContract(IsOneWay=false)]
void MoveForward(int units);
[OperationContract(IsOneWay=false)]
void MoveBackward(int units);
[OperationContract(IsOneWay=false)]
void TurnLeft();
[OperationContract(IsOneWay=false)]
void TurnRight();
}

public interface ISampleClientContract


{
[OperationContract(IsOneWay=false)]
void Position(int x, int y, string direction);
}

If the client and service implement identical contracts, there is no need to define a second contract.
The service contract can reference itself in the CallbackContract  parameter.
[ServiceContract(CallbackContract=typeof(IChat))]
public interface IChat
{
[OperationContract]
void Say(string from, string text);
}

FormatMode: Selecting a Serializer


The serialization of service operation parameters and return values to messages and the subsequent
deserialization are handled by a serializer. The default serializer is XmlFormatter. The other choice is
the .NET Framework'sXmlSerializer.
You can specify a serializer in the [ServiceContract]  attribute by using the FormatMode  parameter. The
choices areContractFormatMode.XmlFormatter  and ContractFormatMode.XmlSerializer. The following
service contract attribute specifies XmlSerializer.
[ServiceContract(FormatMode=ContractFormatMode.XmlSerializer)]
If you serialize using XmlSerializer, only public members of classes are serialized.
Name  and  Namespace: Naming a Service Contract
By default, a service contract takes its name and namespace from the interface or class that
the [ServiceContract]attribute is applied to. To set a different name, use the Name  parameter
of [ServiceContract]. To set a different namespace, use the Namespace  parameter.
[ServiceContract(Namespace="http://tempuri.org", Name="IMyContract"]
interface IMyPrototypeContract
{
...
}

Session: Requiring Sessions


A service contract can indicate that sessions and message ordering must be supported by its binding.
You set theSession  parameter of [ServiceContract]  to true  to require sessions.
[ServiceContract(Session=true)]
public interface IStockAssistant
{
[OperationContract(IsInitiating=true)]
void StartSession(string account);
[OperationContract]
void Buy(string symbol, int shares);
[OperationContract]
void Sell(string symbol, int shares);
[OperationContract(IsTerminating=true)]
void EndSession();
}

Style: Controlling Metadata Style


The way WSDL formats metadata for the service can be set to document/wrapped, document/bare, or
RPC message style. The default is document/wrapped. To change the style, use the Style  parameter at
the [ServiceContract]  or[OperationContract]  level. Possible values
are ServiceOperationStyle.DocumentWrapped,ServiceOperationStyle.DocumentBare,
and ServiceOperationStyle.Rpc. The following code sets the metadata style to document/bare for the
service contract in general, but one operation specifies the RPC style.
[ServiceContract(Style=ServiceOperationStyle.DocumentBare)]
public interface IMyService
{
[OperationContract]
int OperationA();
[OperationContract]
int OperationB();
[OperationContract(Style=ServiceOperationStyle.Rpc)]
int OperationC();
}

Use: Specifying Message Format


Messages can be in either literal or encoded format. In literal format, the body of a message conforms
to an XSD. In encoded format, SOAP encoding is used. The default is literal. If you use the encoded
setting, you must also specifyXmlSerializer.
To change the message format, you set the Use  parameter at
the [ServiceContract]  or [OperationContract]  level. Possible values
are ServiceOperationBindingUse.Literal  and ServiceOperationBindingUse.Encoded.
The following service contract attribute sets the message format to Encoded  and specifies
the XmlSerializer  serializer.
[ServiceContract(Use=ServiceOperationBindingUse.Encoded, FormatMode=
ContractFormatMode.XmlSerializer)]

The [OperationContract] Attribute


The [OperationContract]  attribute marks a method as a service operation and makes it part of the
service contract.
[ServiceContract(Session=true, FormatMode=XmlSerializer)]
public interface IMyService
{
[OperationContract(IsOneWay=true)]
void SubmitExpenseReport(ExpenseReport report);
}

The following parameters can be specified for the [OperationContract] attribute:


Action (string)

Identifies the action to be performed by the service operation. If Action  is not specified, the
default ishttp://tempuri.org/contract-name/operation-name. In an untyped service, you can
set Action  to * to intercept all service operations.

AsyncPattern (boolean)

Defines the service operation as synchronous or asynchronous. An asynchronous service


operation is implemented as a BeginX/EndX  pair in accordance with the general Async Pattern
of the .NET Framework. The default is false  (synchronous).

IsInitiating (boolean)

Determines whether a service operation initiates a new channel on the service. The default
is true  (yes).

IsOneWay (boolean)

Defines the service as one-way or two-way. A one-way service does not send a response and
might not have a return type other than void. A two-way service is accessed in a request-reply
pattern. The default is false(two-way).

IsTerminating (boolean)

Determines whether a service operation will close and shut down the client channel after the
service operation completes and replies. The default is false  (no).

Name (string)

Specifies the name of the service operation. The default is the method name of the service
operation.

ReplyAction (string)

Identifies the action of the response sent by the service operation. If ReplyAction  is not
specified, the default ishttp://tempuri.org/contract-name/operation-nameResponse.

Style (ServiceOperationStyle)
Specifies how the WSDL metadata for the service is formatted. Possible values
are DocumentWrapped,DocumentBare, and Rpc. The default is DocumentWrapped. This
property also exists at the [ServiceContract]level and is described in that section of the
chapter.

Use (ServiceOperationBindingUse)

Specifies whether the message format is literal or encoded. Possible values


are Literal  and Encoded. Usedefaults to Literal. This property also exists at
the [ServiceContract]  level and is described in that section of the chapter.

Action: Specifying Actions


Every service operation has a name in the service contract called an action. Action  is a URI. If you
don't specify an action for a service operation, the name will be http://tempuri.org/contract-
name/operation-name (for example,http://tempuri.org/IWeatherService/ReportLocalConditions).
You can specify an action using the Action=string  parameter of the [OperationContract]  attribute. You
can specify a simple operation name, like RequestReply, or a full URI.
[OperationContract(Action="http://tempuri.org/ISomeContract/SomeOperation")]

In an untyped service operation, you can set Action  to  *  to intercept all messages.
[ServiceContract]
public interface IMyUntypedContract
{
[OperationContract(IsOneWay=false, Action="*")]
Message ProcessMessage(Message message);
}

AsyncPattern: Specifying Asynchronous Service Operations


Service operations can be synchronous or asynchronous. A client calling a two-way service operation
synchronously blocks, waiting for the service operation to complete and send its response. A client
calling a service operation asynchronously doesn't have to wait—it can start a service operation, do
other work, and get the service operation result later.
Asynchronous service operations use the Async Pattern of the .NET Framework. Both clients and
services can use the Async Pattern. For service operations, using AsyncPattern  means breaking up the
service operation intoBegin<operation>  and End<operation>  operations. In the service contract, these
two operations are defined under a single [OperationContract]  attribute with
an AsyncPattern=true  parameter, like this:
[OperationContract(AsyncPattern=true)]
IAsyncResult BeginWeatherReport(string region, int temperature, int windSpeed,
int humidity, float pressure, AsyncCallback callback, object state);
void EndWeatherReport(IAsyncResult ar);

The Begin  operation has two additional parameters beyond what the service operation normally
requires:AsyncCallback  and a state object. The Begin  operation returns an IAsyncResult  rather than
what the service operation would normally return. The End  operation accepts
an IAsyncResult  parameter and returns the service operation result.
For information on using the Async Pattern in clients, see Chapter 6. For information on implementing
the Async Pattern in service operations, see Chapter 7.
IsInitiating  and  IsTerminating: Controlling Session Lifetime
Services can provide session instancing, in which a separate instance of the class is maintained for
each client channel. The IsInitiating  and IsTerminating  parameters allow you to control which service
operations initiate or terminate a session instance. By default, all service operations initiate a session
instance. In the following service contract, the OpenAccount  service operation initiates the sessions
and must be the first service operation a client calls. The CloseAccount  service terminates the session.
[ServiceContract]
public interface IBanking
{
[OperationContract(IsInitiating=true, IsTerminating=false)]
void OpenAccount(account);
[OperationContract(IsInitiating=false, IsTerminating=false)]
void Deposit(decimal amount);
[OperationContract(IsInitiating=false, IsTerminating=false)]
void Withdraw(decimal amount);
[OperationContract(IsInitiating=false, IsTerminating=true)]
void CloseAccount();
}

IsOneWay: Setting Service Operation Direction


A service operation can be one-way or two-way. By default, a service operation is two-way, meaning
that when a service operation is accessed there is both an incoming request communication and an
outgoing reply communication. Even if a service operation has a return type of void, a reply takes
place if it is two-way. When a client invokes a two-way service operation, it waits for the reply before
continuing with its execution. When a client invokes a one-way service operation, it does not wait for a
response.
To specify direction, use the IsOneWay=boolean  parameter of the [OperationContract]  attribute. A
value of trueindicates a one-way service; false  indicates a two-way service. The following service
contract specifies both one-way and two-way service operations.
[ServiceContract]
interface IWeatherReporting
{
[OperationContract(IsOneWay=true)]
void ReportTemperature(int region, float temp);
[OperationContract(IsOneWay=true)]
void ReportWindspeed(int region, int windspeed);
[OperationContract(IsOneWay=false)]
float GetTemperature(int region);
}

Name: Naming a Service Operation


By default, a service operation takes its name from the method name
the [OperationContract]  attribute is applied to. To set a different name, use the Name  parameter
of [OperationContract].
[ServiceContract]
interface IMyPrototypeContract
{
[OperationContract(Name="FunctionB")]
void FunctionA();
}

ReplyAction: Specifying Actions


Just as the request message for a service operation specifies an action, a reply message specifies a
reply action. A reply action is a URI. If you don't specify a reply action for a service operation, it will be
namedhttp://tempuri.org/contract-name/operation-nameResponse (for
example,http://tempuri.org/IWeatherService/ReportLocalConditionsResponse).
You can specify a reply action by using the ReplyAction=string  parameter of
the [OperationContract]  attribute. You can specify a simple operation name,
like RequestReplyResponse, or a full URI.
[OperationContract(ReplyAction="http://tempuri.org/ISomeContract/SomeOperationResp
onse")]

The [BindingRequirements] Attribute


The [BindingRequirements]  attribute specifies requirements for a service contract that bindings must
meet. An error occurs if an attempt is made to create an endpoint whose binding doesn't match the
requirements. Like the[ServiceContract]  attribute, [BindingRequirements]  is applied to the interface or
class that defines the service contract.
[ServiceContract(Session=true)]
[BindingRequirements(
CommunicationProfileRequirements=
CommunicationProfileRequirements.DotNet,
TransactionFlowRequirements=RequirementsMode.Require,
QueuedDeliveryRequirements=RequirementsMode.Require,
RequireOrderedDelivery=true)]
{
...
}

The following parameters can be specified for the [BindingRequirements]  attribute:


CommunicationProfileRequirements (CommunicationProfileRequirements)

Identifies a specific binding profile that must be used. Possible values


are BasicProfile, WS, DotNet, and None. The default
is CommunicationProfileRequirements.None,  which allows bindings of any profile to be used.

QueuedDeliveryRequirements (RequirementsMode)

Specifies whether queuing is required, prohibited, or permitted. Possible values


are Required, Disallowed, andIgnore. The default is RequirementsMode.Ignore, which specifies
no queuing requirements.

RequireOrderedDelivery (boolean)

Specifies whether ordered delivery of messages is required. Possible values are true  and false.
The default isfalse, which means ordered delivery is not required.

TransactionFlowRequirements (RequirementsMode)

Specifies whether transaction flow is required, prohibited, or permitted. Possible values


are Required,Disallowed, and Ignore. The default is RequirementsMode.Ignore, which specifies
no transaction flow requirements.

CommunicationProfileRequirements: Requiring a Profile


By default, bindings for a service contract can be of any profile: BasicProfile, WSProfile, NetProfile, or
custom. To require a specific profile, use the CommunicationProfileRequirements  parameter
of [BindingRequirements]. Possible values are BasicProfile, WS, DotNet, and None. The default is None.
[ServiceContract]
[BindingRequirements(CommunicationProfileRequirements=CommunicationProfileRequirem
ents.DotNet)]
Public interface IMyContract
{
[OperationContract]
void DoSomething();
}

QueuedDeliveryRequirements: Queuing Control


By default, bindings for a service contract are free to use the MSMQ transport or not. To specifically
require or disallow the use of queuing in a binding, use the QueuedDeliveryRequirements  parameter
of [BindingRequirements]. Possible values are Required, Disallowed, and Ignore. The default is Ignore.
[ServiceContract]
[BindingRequirements(QueuedDeliveryRequirements=RequirementsMode.Required)]
Public interface IMyContract
{
[OperationContract]
void DoSomething();
}

RequireOrderedDelivery: Ordered Delivery Control


By default, bindings for a service contract can provide ordered delivery assurances or not. To
specifically require or disallow ordered delivery, use the RequireOrderedDelivery  parameter
of [BindingRequirements]. Possible values aretrue  and false. The default is false.
[ServiceContract]
[BindingRequirements(RequireOrderedDelivery=true)]
Public interface IMyContract
{
[OperationContract]
void DoSomething();
}

TransactionFlowRequirements: Transaction Flow Control


By default, bindings for a service contract are free to enable transaction flow or not. To specifically
require or disallow transaction flow in a binding, use the TransactionFlowRequirements  parameter
of [BindingRequirements]. Possible values are Required, Disallowed, and Ignore. The default is Ignore.
[ServiceContract]
[BindingRequirements(TransactionFlowRequirements=RequirementsMode.Required)]
Public interface IMyContract
{
[OperationContract]
void DoSomething();
}

Defining Service Operations


There are three kinds of services:
 Typed In a typed service, the parameters and return values of service operations are primitive
or complex data types.
 Untyped In an untyped service, the parameters and return values of service operations are
messages.

 Typed message In a typed message service, the parameters and return values of service
operations are custom messages defined with message contracts.
Defining Typed Service Operations
A typed service operation is one that accepts simple or complex data as parameters and return values.
Using a typed service operation feels very much like using a method of a class. The following service
contract contains typed service operations.
[ServiceContract]
public interface IMeasurementConversion
{
[OperationContract]
float CalculateDistance(float x1, float y1, float x2, float y2);
[OperationContract]
int CalculateShippingZone(float x1, float y1);
}

Service operations can return values, as the preceding service operations do. But sometimes you
might need to return more than one value or return a modified version of a parameter value. In these
cases, you can use ref  andout  parameters, just as you would in object-oriented programming. In
actuality, you are passing by value. The following service operations use ref  and out  parameters.
[ServiceContract]
public interface IMeasurementConversion
{
[OperationContract]
void MoveNorth(ref float x, ref float y, float distance);
[OperationContract]
void MoveSouth(ref float x, ref float y, float distance);
[OperationContract]
void MoveEast(ref float x, ref float y, float distance);
[OperationContract]
void MoveWest(ref float x, ref float y, float distance);
[OperationContract]
void CalculateDistance(float x1, float y1, float x2, float y2, out float
distance);
}

In addition to simple types and arrays, typed service operations can also accept complex data
structures as parameters or return them as results. This requires the use of data contracts. See the
earlier section titled "Understanding Data Contracts" and the later section titled "Programming Data
Contracts" for more information.
Defining Untyped Service Operations
An untyped service operation accepts a Message  parameter, which allows it to accept any kind of
incoming message. The service operation code is responsible for interpreting the message. If the
service operation sends a reply, it is also of type Message. The following service contract contains an
untyped service operation.
[ServiceContract]
public interface IMyUntypedContract
{
[OperationContract(IsOneWay=true, Action="*")]
void ProcessMessage(Message message);
}

Specifying the Action=?*?  parameter in the [OperationContract]  attribute causes the service operation


to receive all incoming messages regardless of the service operation name originally specified by the
client.
For information on how to create messages to send or interpret received messages
using Message  objects, see Chapter 7.
Defining Typed Message Service Operations
A typed message service operation accepts a custom message parameter—a message contract class.
The service operation code is responsible for interpreting the message. If the service operation sends
a reply, the reply is also a custom message, but it does not have to be the same type as the request
message. The following code contains a service contract with a typed message service operation and a
message contract defining a custom message.
[ServiceContract]
public interface IMyTypedMessageContract
{
[OperationContract(IsOneWay=true, Action="*")]
void Register(Contact contact);
}

[MessageContract]
public class Contact
{
[MessageHeader]
string LastName;
[MessageHeader]
string FirstName;
[MessageBody]
string Phone;
[MessageBody]
string Email;
[MessageBody]
string Notes;
}

For information on how to create message contracts, see the section titled "Programming Message
Contracts" later in this chapter. For information on how to implement typed message service
operations, see Chapter 7.

Вам также может понравиться