Dubbelzijdige services
Een dubbelzijdig servicecontract is een patroon voor het uitwisselen van berichten waarin beide eindpunten berichten onafhankelijk naar de andere kunnen verzenden. Een duplex-service kan daarom berichten terugsturen naar het clienteindpunt, wat gebeurtenisachtig gedrag biedt. Dubbelzijdige communicatie vindt plaats wanneer een client verbinding maakt met een service en de service een kanaal biedt waarop de service berichten naar de client kan verzenden. Houd er rekening mee dat het gebeurtenisachtige gedrag van duplexservices alleen binnen een sessie werkt.
Als u een dubbelzijdig contract wilt maken, maakt u een paar interfaces. De eerste is de interface van het servicecontract waarin de bewerkingen worden beschreven die een client kan aanroepen. Dit servicecontract moet een callback-contract in de ServiceContractAttribute.CallbackContract eigenschap opgeven. Het callback-contract is de interface waarmee de bewerkingen worden gedefinieerd die de service kan aanroepen op het clienteindpunt. Voor een duplex-contract is geen sessie vereist, hoewel de door het systeem geleverde duplexbindingen er gebruik van maken.
Hier volgt een voorbeeld van een dubbelzijdig contract.
[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);
}
public interface ICalculatorDuplexCallback
{
[OperationContract(IsOneWay = true)]
void Equals(double result);
[OperationContract(IsOneWay = true)]
void Equation(string eqn);
}
<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
Public Interface ICalculatorDuplexCallback
<OperationContract(IsOneWay:=True)> _
Sub Equals(ByVal result As Double)
<OperationContract(IsOneWay:=True)> _
Sub Equation(ByVal eqn As String)
End Interface
De CalculatorService
klasse implementeert de primaire ICalculatorDuplex
interface. De service gebruikt de PerSession instantiemodus om het resultaat voor elke sessie te behouden. Een privé-eigenschap met de naam Callback
opent het callback-kanaal naar de client. De service gebruikt de callback voor het terugsturen van berichten naar de client via de callback-interface, zoals wordt weergegeven in de volgende voorbeeldcode.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class CalculatorService : ICalculatorDuplex
{
double result = 0.0D;
string equation;
public CalculatorService()
{
equation = result.ToString();
}
public void Clear()
{
Callback.Equation(equation + " = " + result.ToString());
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);
}
ICalculatorDuplexCallback Callback
{
get
{
return OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>();
}
}
}
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)> _
Public Class CalculatorService
Implements ICalculatorDuplex
Private result As Double = 0.0R
Private equation As String
Public Sub New()
equation = result.ToString()
End Sub
Public Sub Clear() Implements ICalculatorDuplex.Clear
Callback.Equation(equation & " = " & result.ToString())
equation = result.ToString()
End Sub
Public Sub AddTo(ByVal n As Double) Implements ICalculatorDuplex.AddTo
result += n
equation &= " + " & n.ToString()
CType(Callback, Object).Equals(result)
End Sub
Public Sub SubtractFrom(ByVal n As Double) Implements ICalculatorDuplex.SubtractFrom
result -= n
equation &= " - " & n.ToString()
CType(Callback, Object).Equals(result)
End Sub
Public Sub MultiplyBy(ByVal n As Double) Implements ICalculatorDuplex.MultiplyBy
result *= n
equation &= " * " & n.ToString()
CType(Callback, Object).Equals(result)
End Sub
Public Sub DivideBy(ByVal n As Double) Implements ICalculatorDuplex.DivideBy
result /= n
equation &= " / " & n.ToString()
CType(Callback, Object).Equals(result)
End Sub
Private ReadOnly Property Callback() As ICalculatorDuplexCallback
Get
Return OperationContext.Current.GetCallbackChannel(Of ICalculatorDuplexCallback)()
End Get
End Property
End Class
De client moet een klasse opgeven waarmee de callback-interface van het duplex-contract wordt geïmplementeerd voor het ontvangen van berichten van de service. In de volgende voorbeeldcode ziet u een CallbackHandler
klasse waarmee de ICalculatorDuplexCallback
interface wordt geïmplementeerd.
public class CallbackHandler : ICalculatorDuplexCallback
{
public void Equals(double result)
{
Console.WriteLine("Equals({0})", result);
}
public void Equation(string eqn)
{
Console.WriteLine("Equation({0})", eqn);
}
}
Public Class CallbackHandler
Implements ICalculatorDuplexCallback
Public Overridable Shadows Sub Equals(ByVal result As Double) Implements ICalculatorDuplexCallback.Equals
Console.WriteLine("Equals({0})", result)
End Sub
Public Sub Equation(ByVal eqn As String) Implements ICalculatorDuplexCallback.Equation
Console.WriteLine("Equation({0})", eqn)
End Sub
End Class
Voor de WCF-client die wordt gegenereerd voor een duplex-contract, moet een InstanceContext klasse worden geleverd bij de bouw. Deze InstanceContext klasse wordt gebruikt als de site voor een object dat de callback-interface implementeert en berichten verwerkt die vanuit de service worden teruggestuurd. Een InstanceContext klasse wordt samengesteld met een exemplaar van de CallbackHandler
klasse. Dit object verwerkt berichten die vanuit de service naar de client worden verzonden op de callback-interface.
// Construct InstanceContext to handle messages on callback interface
InstanceContext instanceContext = new InstanceContext(new CallbackHandler());
// Create a client
CalculatorDuplexClient client = new CalculatorDuplexClient(instanceContext);
' Construct InstanceContext to handle messages on callback interface
Dim instanceContext As New InstanceContext(New CallbackHandler())
' Create a client
Dim client As New CalculatorDuplexClient(instanceContext)
De configuratie voor de service moet worden ingesteld om een binding te bieden die zowel sessiecommunicatie als dubbelzijdige communicatie ondersteunt. Het wsDualHttpBinding
element ondersteunt sessiecommunicatie en maakt dubbelzijdige communicatie mogelijk door dubbele HTTP-verbindingen te bieden, één voor elke richting.
Op de client moet u een adres configureren dat de server kan gebruiken om verbinding te maken met de client, zoals wordt weergegeven in de volgende voorbeeldconfiguratie.
Notitie
Niet-duplex-clients die zich niet verifiëren met behulp van een beveiligd gesprek, gooien meestal een MessageSecurityException. Als echter een duplex-client die gebruikmaakt van een beveiligd gesprek niet kan worden geverifieerd, ontvangt de client een TimeoutException in plaats daarvan.
Als u een client/service maakt met behulp van het WSHttpBinding
element en u het callback-eindpunt van de client niet opneemt, wordt de volgende fout weergegeven.
HTTP could not register URL
htp://+:80/Temporary_Listen_Addresses/<guid> because TCP port 80 is being used by another application.
De volgende voorbeeldcode laat zien hoe u programmatisch het adres van het clienteindpunt opgeeft.
WSDualHttpBinding binding = new WSDualHttpBinding();
EndpointAddress endptadr = new EndpointAddress("http://localhost:12000/DuplexTestUsingCode/Server");
binding.ClientBaseAddress = new Uri("http://localhost:8000/DuplexTestUsingCode/Client/");
Dim binding As New WSDualHttpBinding()
Dim endptadr As New EndpointAddress("http://localhost:12000/DuplexTestUsingCode/Server")
binding.ClientBaseAddress = New Uri("http://localhost:8000/DuplexTestUsingCode/Client/")
In de volgende voorbeeldcode ziet u hoe u het adres van het clienteindpunt in de configuratie opgeeft.
<client>
<endpoint name ="ServerEndpoint"
address="http://localhost:12000/DuplexTestUsingConfig/Server"
bindingConfiguration="WSDualHttpBinding_IDuplexTest"
binding="wsDualHttpBinding"
contract="IDuplexTest" />
</client>
<bindings>
<wsDualHttpBinding>
<binding name="WSDualHttpBinding_IDuplexTest"
clientBaseAddress="http://localhost:8000/myClient/" >
<security mode="None"/>
</binding>
</wsDualHttpBinding>
</bindings>
Waarschuwing
Het duplex-model detecteert niet automatisch wanneer een service of client het kanaal sluit. Dus als een client onverwacht wordt beëindigd, wordt de service standaard niet op de hoogte gesteld of als een service onverwacht wordt beëindigd, wordt de client niet op de hoogte gesteld. Als u een service gebruikt die niet is verbonden, wordt de CommunicationException uitzondering gegenereerd. Clients en services kunnen hun eigen protocol implementeren om elkaar op de hoogte te stellen als ze dat willen. Zie WCF-foutafhandeling voor meer informatie over foutafhandeling.