Практическое руководство. Создание дуплексного контракта

В этом разделе приведены основные этапы создания методов, использующих дуплексные (двухсторонние) контракты. Дуплексный контракт позволяет клиентам и серверам взаимодействовать друг с другом независимо (клиент может инициировать вызовы сервера, а сервер - вызовы клиента). Дуплексный контракт — это один из трех шаблонов сообщений, доступных службам Windows Communication Foundation (WCF). Две другие схемы обмена сообщениями - это односторонний обмен и запрос-ответ. Дуплексный контракт состоит из двух односторонних контрактов между клиентом и сервером, при этом не требуется корреляция между вызовами методов. Контракт этого типа следует использовать, если служба должна запрашивать у клиента дополнительную информацию или в явном виде создавать события в клиенте. Дополнительные сведения о создании клиентского приложения для дуплексного контракта см. в статье "Практическое руководство. Службы Access с дуплексным контрактом". Рабочий пример см. в примере Duplex .

Создание двухстороннего контракта

  1. Создайте интерфейс, образующий серверную часть дуплексного контракта.

  2. Примените класс ServiceContractAttribute к интерфейсу.

    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required,
                     CallbackContract=typeof(ICalculatorDuplexCallback))]
    public interface ICalculatorDuplex
    {
        [OperationContract(IsOneWay=true)]
        void Clear();
        [OperationContract(IsOneWay = true)]
        void AddTo(double n);
        [OperationContract(IsOneWay = true)]
        void SubtractFrom(double n);
        [OperationContract(IsOneWay = true)]
        void MultiplyBy(double n);
        [OperationContract(IsOneWay = true)]
        void DivideBy(double n);
    }
    
    <ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples", SessionMode:=SessionMode.Required, _
                    CallbackContract:=GetType(ICalculatorDuplexCallback))> _
    Public Interface ICalculatorDuplex
    
        <OperationContract(IsOneWay:=True)> _
        Sub Clear()
        <OperationContract(IsOneWay:=True)> _
        Sub AddTo(ByVal n As Double)
        <OperationContract(IsOneWay:=True)> _
        Sub SubtractFrom(ByVal n As Double)
        <OperationContract(IsOneWay:=True)> _
        Sub MultiplyBy(ByVal n As Double)
        <OperationContract(IsOneWay:=True)> _
        Sub DivideBy(ByVal n As Double)
    End Interface
    
    
  3. Объявите в интерфейсе подписи методов.

  4. Примените класс OperationContractAttribute к каждой подписи метода, которая должна входить в открытый контракт.

  5. Создайте интерфейс обратного вызова, определяющий набор операций, которые служба может вызывать в клиенте.

    public interface ICalculatorDuplexCallback
    {
        [OperationContract(IsOneWay = true)]
        void Equals(double result);
        [OperationContract(IsOneWay = true)]
        void Equation(string eqn);
    }
    
    Public Interface ICalculatorDuplexCallback
        <OperationContract(IsOneWay:=True)> _
        Sub Equals(ByVal result As Double)
        <OperationContract(IsOneWay:=True)> _
        Sub Equation(ByVal eqn As String)
    end interface
    
    
  6. Объявите подписи методов в интерфейсе обратного вызова.

  7. Примените класс OperationContractAttribute к каждой подписи метода, которая должна входить в открытый контракт.

  8. Объедините эти два интерфейса в дуплексный контракт, задав для свойства CallbackContract в основном интерфейсе тип интерфейса обратного вызова.

Вызов методов в клиенте

  1. В реализации основного контракта на стороне службы объявите переменную для интерфейса обратного вызова.

  2. Присвойте переменной ссылку на объект, возвращаемый методом GetCallbackChannel класса OperationContext.

    ICalculatorDuplexCallback callback = null;
    
    Dim callback As ICalculatorDuplexCallback
    
    callback = OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>();
    
    callback = OperationContext.Current.GetCallbackChannel(Of ICalculatorDuplexCallback)()
    
  3. Вызовите методы, определенные в интерфейсе обратного вызова.

Пример

В следующем примере кода демонстрируется дуплексное взаимодействие. Контракт службы содержит операции службы для перемещения вперед или назад. Контракт клиента содержит операцию службы для сообщения о положении.

// Define a duplex service contract.
// A duplex contract consists of two interfaces.
// The primary interface is used to send messages from client to service.
// The callback interface is used to send messages from service back to client.
// ICalculatorDuplex allows one to perform multiple operations on a running result.
// The result is sent back after each operation on the ICalculatorCallback interface.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required,
                 CallbackContract=typeof(ICalculatorDuplexCallback))]
public interface ICalculatorDuplex
{
    [OperationContract(IsOneWay=true)]
    void Clear();
    [OperationContract(IsOneWay = true)]
    void AddTo(double n);
    [OperationContract(IsOneWay = true)]
    void SubtractFrom(double n);
    [OperationContract(IsOneWay = true)]
    void MultiplyBy(double n);
    [OperationContract(IsOneWay = true)]
    void DivideBy(double n);
}
// The callback interface is used to send messages from service back to client.
// The Equals operation will return the current result after each operation.
// The Equation operation will return the complete equation after Clear() is called.
public interface ICalculatorDuplexCallback
{
    [OperationContract(IsOneWay = true)]
    void Equals(double result);
    [OperationContract(IsOneWay = true)]
    void Equation(string eqn);
}
// Service class which implements a duplex service contract.
// Use an InstanceContextMode of PerSession to store the result
// An instance of the service will be bound to each duplex session
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class CalculatorService : ICalculatorDuplex
{
    double result;
    string equation;
    ICalculatorDuplexCallback callback = null;

    public CalculatorService()
    {
        result = 0.0D;
        equation = result.ToString();
        callback = OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>();
    }

    public void Clear()
    {
        callback.Equation(equation + " = " + result.ToString());
        result = 0.0D;
        equation = result.ToString();
    }

    public void AddTo(double n)
    {
        result += n;
        equation += " + " + n.ToString();
        callback.Equals(result);
    }

    public void SubtractFrom(double n)
    {
        result -= n;
        equation += " - " + n.ToString();
        callback.Equals(result);
    }

    public void MultiplyBy(double n)
    {
        result *= n;
        equation += " * " + n.ToString();
        callback.Equals(result);
    }

    public void DivideBy(double n)
    {
        result /= n;
        equation += " / " + n.ToString();
        callback.Equals(result);
    }
}
' Define a duplex service contract.
' A duplex contract consists of two interfaces.
' The primary interface is used to send messages from client to service.
' The callback interface is used to send messages from service back to client.
' ICalculatorDuplex allows one to perform multiple operations on a running result.
' The result is sent back after each operation on the ICalculatorCallback interface.
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples", SessionMode:=SessionMode.Required, _
                CallbackContract:=GetType(ICalculatorDuplexCallback))> _
Public Interface ICalculatorDuplex

    <OperationContract(IsOneWay:=True)> _
    Sub Clear()
    <OperationContract(IsOneWay:=True)> _
    Sub AddTo(ByVal n As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub SubtractFrom(ByVal n As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub MultiplyBy(ByVal n As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub DivideBy(ByVal n As Double)
End Interface

' The callback interface is used to send messages from service back to client.
' The Equals operation will return the current result after each operation.
' The Equation operation will return the complete equation after Clear() is called.
Public Interface ICalculatorDuplexCallback
    <OperationContract(IsOneWay:=True)> _
    Sub Equals(ByVal result As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub Equation(ByVal eqn As String)
end interface

' Service class which implements a duplex service contract.
' Use an InstanceContextMode of PerSession to store the result
' An instance of the service will be bound to each duplex session
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)> _
Public Class CalculatorService
    Implements ICalculatorDuplex
    Dim result As Double
    Dim equation As String
    Dim callback As ICalculatorDuplexCallback

    Public Sub New()
        result = 0D
        equation = result.ToString()
        callback = OperationContext.Current.GetCallbackChannel(Of ICalculatorDuplexCallback)()

    End Sub
    Public Sub AddTo(ByVal n As Double) Implements ICalculatorDuplex.AddTo
        result += n
        equation += " + " + n.ToString()
        callback.Equals(result)
    End Sub

    Public Sub Clear() Implements ICalculatorDuplex.Clear
        callback.Equation(equation + " = " + result.ToString())
        result = 0D
        equation = result.ToString()
    End Sub

    Public Sub DivideBy(ByVal n As Double) Implements ICalculatorDuplex.DivideBy
        result /= n
        equation += " / " + n.ToString()
        callback.Equals(result)
    End Sub

    Public Sub MultiplyBy(ByVal n As Double) Implements ICalculatorDuplex.MultiplyBy
        result *= n
        equation += " * " + n.ToString()
        callback.Equals(result)
    End Sub

    Public Sub SubtractFrom(ByVal n As Double) Implements ICalculatorDuplex.SubtractFrom
        result -= n
        equation += " - " + n.ToString()
        callback.Equals(result)
    End Sub
End Class
  • Применение атрибутов ServiceContractAttribute и OperationContractAttribute обеспечивает автоматическое создание определения контракта службы в языке описания служб (WSDL).

  • Используйте средство служебной программы метаданных ServiceModel (Svcutil.exe), чтобы получить документ WSDL и (необязательно) код и конфигурацию для клиента.

  • Конечные точки, предоставляющие дуплексные службы, должны быть безопасными. Когда служба получает дуплексное сообщение, она проверяет элемент ReplyTo входящего сообщения, чтобы определить, куда отправлять ответ. Если канал не является безопасным, ненадежный клиент может послать вредоносное сообщение с указанием компьютера для атаки в элементе ReplyTo, что приведет к атаке типа "отказ в обслуживании" на этот компьютер. В случае обычной передачи сообщений по схеме "запрос-ответ" это не является проблемой, так как элемент ReplyTo игнорируется, а ответное сообщение передается по тому каналу, по которому поступило исходное сообщение.

См. также