Procedimiento para intercambiar mensajes en cola con puntos de conexión de WCF
Las colas garantizan que la mensajería de confianza puede tener lugar entre un cliente y un servicio de Windows Communication Foundation (WCF), aun cuando el servicio no esté disponible en el momento de la comunicación. Los procedimientos siguientes muestran cómo garantizar una comunicación duradera entre un cliente y un servicio utilizando el enlace en cola estándar al implementar el servicio WCF.
En esta sección se explica cómo utilizar NetMsmqBinding para la comunicación en cola entre un cliente WCF y un servicio WCF.
Para utilizar la puesta en cola en un servicio WCF
Defina un contrato de servicios utilizando una interfaz marcado con ServiceContractAttribute. Marque las operaciones en la interfaz que forman parte del contrato de servicios con OperationContractAttribute y especifíquelas como unidireccionales porque no se devuelven respuestas al método. El código siguiente proporciona un contrato de servicios de ejemplo y su definición de operación.
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")] public interface IOrderProcessor { [OperationContract(IsOneWay = true)] void SubmitPurchaseOrder(PurchaseOrder po); }
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _ Public Interface IOrderProcessor <OperationContract(IsOneWay:=True)> _ Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder) End Interface
Si el contrato de servicio pasa los tipos definidos por el usuario, deberá definir los contratos de datos para esos tipos. El ejemplo de código siguiente muestra dos contratos de datos,
PurchaseOrder
yPurchaseOrderLineItem
. Estos dos tipos definen los datos que se envían al servicio. (Tenga en cuenta que las clases que definen este contrato de datos también definen una serie de métodos. Estos métodos no se consideran parte del contrato de datos. Solo los miembros declarados con el atributo DataMemberAttribute forman parte del contrato de datos).[DataContract(Namespace = "http://Microsoft.ServiceModel.Samples")] public class PurchaseOrder { static readonly string[] OrderStates = { "Pending", "Processed", "Shipped" }; static Random statusIndexer = new Random(137); [DataMember] public string PONumber; [DataMember] public string CustomerId; [DataMember] public PurchaseOrderLineItem[] orderLineItems; public float TotalCost { get { float totalCost = 0; foreach (PurchaseOrderLineItem lineItem in orderLineItems) totalCost += lineItem.TotalCost; return totalCost; } } public string Status { get { return OrderStates[statusIndexer.Next(3)]; } } public override string ToString() { System.Text.StringBuilder strbuf = new System.Text.StringBuilder("Purchase Order: " + PONumber + "\n"); strbuf.Append("\tCustomer: " + CustomerId + "\n"); strbuf.Append("\tOrderDetails\n"); foreach (PurchaseOrderLineItem lineItem in orderLineItems) { strbuf.Append("\t\t" + lineItem.ToString()); } strbuf.Append("\tTotal cost of this order: $" + TotalCost + "\n"); strbuf.Append("\tOrder status: " + Status + "\n"); return strbuf.ToString(); } }
<DataContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _ Public Class PurchaseOrder Private Shared ReadOnly OrderStates() As String = {"Pending", "Processed", "Shipped"} Private Shared statusIndexer As New Random(137) <DataMember> _ Public PONumber As String <DataMember> _ Public CustomerId As String <DataMember> _ Public orderLineItems() As PurchaseOrderLineItem Public ReadOnly Property TotalCost() As Single Get Dim totalCost_Renamed As Single = 0 For Each lineItem In orderLineItems totalCost_Renamed += lineItem.TotalCost Next lineItem Return totalCost_Renamed End Get End Property Public ReadOnly Property Status() As String Get Return OrderStates(statusIndexer.Next(3)) End Get End Property Public Overrides Function ToString() As String Dim strbuf As New System.Text.StringBuilder("Purchase Order: " & PONumber & Constants.vbLf) strbuf.Append(Constants.vbTab & "Customer: " & CustomerId & Constants.vbLf) strbuf.Append(Constants.vbTab & "OrderDetails" & Constants.vbLf) For Each lineItem In orderLineItems strbuf.Append(Constants.vbTab + Constants.vbTab + lineItem.ToString()) Next lineItem strbuf.Append(Constants.vbTab & "Total cost of this order: $" & TotalCost + Constants.vbLf) strbuf.Append(Constants.vbTab & "Order status: " & Status + Constants.vbLf) Return strbuf.ToString() End Function End Class
Implemente los métodos del contrato de servicios definidos en la interfaz en una clase.
public class OrderProcessorService : IOrderProcessor { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public void SubmitPurchaseOrder(PurchaseOrder po) { Orders.Add(po); Console.WriteLine("Processing {0} ", po); } }
Public Class OrderProcessorService Implements IOrderProcessor <OperationBehavior(TransactionScopeRequired:=True, TransactionAutoComplete:=True)> _ Public Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder) Implements IOrderProcessor.SubmitPurchaseOrder Orders.Add(po) Console.WriteLine("Processing {0} ", po) End Sub End Class
Observe el atributo OperationBehaviorAttribute colocado en el método
SubmitPurchaseOrder
. Esto especifica que se debe llamar a esta operación dentro de una transacción, y que esta finaliza automáticamente cuando se completa el método.Cree una cola transaccional utilizando System.Messaging. Puede decidir crear la cola usando Microsoft Message Queuing (MSMQ) Microsoft Management Console (MMC) en su lugar. En ese caso, asegúrese de crear una cola transaccional.
// Create the transacted MSMQ queue if necessary. if (!MessageQueue.Exists(queueName)) MessageQueue.Create(queueName, true);
' Create the transacted MSMQ queue if necessary. If (Not MessageQueue.Exists(queueName)) Then MessageQueue.Create(queueName, True) End If
Defina ServiceEndpoint en configuración que especifique la dirección de servicio y use el enlace NetMsmqBinding estándar. Para más información sobre el uso de la configuración de WCF, consulte Configuración de servicios WCF.
Cree un host para el servicio
OrderProcessing
utilizando ServiceHost que lea los mensajes de la cola y los procese. Abra el host de servicio para hacer que el servicio esté disponible. Muestre un mensaje que indique al usuario que debe presionar una tecla para finalizar el servicio. Llame aReadLine
para esperar a que se presione una tecla y, a continuación, cierre el servicio.// Create a ServiceHost for the OrderProcessorService type. using (ServiceHost serviceHost = new ServiceHost(typeof(OrderProcessorService))) { // Open the ServiceHost to create listeners and start listening for messages. serviceHost.Open(); // The service can now be accessed. Console.WriteLine("The service is ready."); Console.WriteLine("Press <ENTER> to terminate service."); Console.WriteLine(); Console.ReadLine(); // Close the ServiceHostB to shutdown the service. serviceHost.Close(); }
' Create a ServiceHost for the OrderProcessorService type. Using serviceHost As New ServiceHost(GetType(OrderProcessorService)) ' Open the ServiceHost to create listeners and start listening for messages. serviceHost.Open() ' The service can now be accessed. Console.WriteLine("The service is ready.") Console.WriteLine("Press <ENTER> to terminate service.") Console.WriteLine() Console.ReadLine() ' Close the ServiceHostB to shutdown the service. serviceHost.Close() End Using
Para crear un cliente para el servicio en cola
En el siguiente ejemplo se muestra cómo ejecutar la aplicación de hospedaje y cómo usar la herramienta Svcutil.exe para crear el cliente WCF.
svcutil http://localhost:8000/ServiceModelSamples/service
Defina una clase ServiceEndpoint en la configuración que especifique la dirección y use el enlace NetMsmqBinding, tal y como se muestra en el ejemplo siguiente.
Cree un ámbito de la transacción para escribir en la cola transaccional, llame a la operación
SubmitPurchaseOrder
y cierre el cliente WCF, tal y como se muestra en el ejemplo siguiente.//Create a transaction scope. using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) { // Make a queued call to submit the purchase order. client.SubmitPurchaseOrder(po); // Complete the transaction. scope.Complete(); } //Closing the client gracefully closes the connection and cleans up resources. client.Close();
'Create a transaction scope. Using scope As New TransactionScope(TransactionScopeOption.Required) ' Make a queued call to submit the purchase order. client.SubmitPurchaseOrder(po) ' Complete the transaction. scope.Complete() End Using 'Closing the client gracefully closes the connection and cleans up resources. client.Close()
Ejemplo
Los ejemplos siguientes muestran el código de servicio, la aplicación de hospedaje, el archivo App.config y el código de cliente incluidos en este ejemplo.
// This is the service code
// Copyright (c) Microsoft Corporation. All Rights Reserved.
using System;
using System.ServiceModel.Channels;
using System.Configuration;
using System.Messaging;
using System.ServiceModel;
using System.Transactions;
using System.Runtime.Serialization;
using System.Collections.Generic;
namespace Microsoft.ServiceModel.Samples
{
// Define the purchase order line item.
[DataContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public class PurchaseOrderLineItem
{
[DataMember]
public string ProductId;
[DataMember]
public float UnitCost;
[DataMember]
public int Quantity;
public override string ToString()
{
String displayString = "Order LineItem: " + Quantity + " of " + ProductId + " @unit price: $" + UnitCost + "\n";
return displayString;
}
public float TotalCost
{
get { return UnitCost * Quantity; }
}
}
// Define the purchase order.
[DataContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public class PurchaseOrder
{
static readonly string[] OrderStates = { "Pending", "Processed", "Shipped" };
static Random statusIndexer = new Random(137);
[DataMember]
public string PONumber;
[DataMember]
public string CustomerId;
[DataMember]
public PurchaseOrderLineItem[] orderLineItems;
public float TotalCost
{
get
{
float totalCost = 0;
foreach (PurchaseOrderLineItem lineItem in orderLineItems)
totalCost += lineItem.TotalCost;
return totalCost;
}
}
public string Status
{
get
{
return OrderStates[statusIndexer.Next(3)];
}
}
public override string ToString()
{
System.Text.StringBuilder strbuf = new System.Text.StringBuilder("Purchase Order: " + PONumber + "\n");
strbuf.Append("\tCustomer: " + CustomerId + "\n");
strbuf.Append("\tOrderDetails\n");
foreach (PurchaseOrderLineItem lineItem in orderLineItems)
{
strbuf.Append("\t\t" + lineItem.ToString());
}
strbuf.Append("\tTotal cost of this order: $" + TotalCost + "\n");
strbuf.Append("\tOrder status: " + Status + "\n");
return strbuf.ToString();
}
}
// Order Processing Logic
// Can replace with transaction-aware resource such as SQL or transacted hashtable to hold the purchase orders.
// This example uses a non-transactional resource.
public class Orders
{
static Dictionary<string, PurchaseOrder> purchaseOrders = new Dictionary<string, PurchaseOrder>();
public static void Add(PurchaseOrder po)
{
purchaseOrders.Add(po.PONumber, po);
}
public static string GetOrderStatus(string poNumber)
{
PurchaseOrder po;
if (purchaseOrders.TryGetValue(poNumber, out po))
return po.Status;
else
return null;
}
public static void DeleteOrder(string poNumber)
{
if(purchaseOrders[poNumber] != null)
purchaseOrders.Remove(poNumber);
}
}
// Define a service contract.
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public interface IOrderProcessor
{
[OperationContract(IsOneWay = true)]
void SubmitPurchaseOrder(PurchaseOrder po);
}
// Service class that implements the service contract.
// Added code to write output to the console window.
public class OrderProcessorService : IOrderProcessor
{
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void SubmitPurchaseOrder(PurchaseOrder po)
{
Orders.Add(po);
Console.WriteLine("Processing {0} ", po);
}
}
}
' This is the service code
' Copyright (c) Microsoft Corporation. All Rights Reserved.
Imports System.ServiceModel.Channels
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.Transactions
Imports System.Runtime.Serialization
Imports System.Collections.Generic
Namespace Microsoft.ServiceModel.Samples
' Define the purchase order line item.
<DataContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
Public Class PurchaseOrderLineItem
<DataMember> _
Public ProductId As String
<DataMember> _
Public UnitCost As Single
<DataMember> _
Public Quantity As Integer
Public Overrides Function ToString() As String
Dim displayString As String = "Order LineItem: " & Quantity & " of " & ProductId & " @unit price: $" & UnitCost + Constants.vbLf
Return displayString
End Function
Public ReadOnly Property TotalCost() As Single
Get
Return UnitCost * Quantity
End Get
End Property
End Class
' Define the purchase order.
<DataContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
Public Class PurchaseOrder
Private Shared ReadOnly OrderStates() As String = {"Pending", "Processed", "Shipped"}
Private Shared statusIndexer As New Random(137)
<DataMember> _
Public PONumber As String
<DataMember> _
Public CustomerId As String
<DataMember> _
Public orderLineItems() As PurchaseOrderLineItem
Public ReadOnly Property TotalCost() As Single
Get
Dim totalCost_Renamed As Single = 0
For Each lineItem In orderLineItems
totalCost_Renamed += lineItem.TotalCost
Next lineItem
Return totalCost_Renamed
End Get
End Property
Public ReadOnly Property Status() As String
Get
Return OrderStates(statusIndexer.Next(3))
End Get
End Property
Public Overrides Function ToString() As String
Dim strbuf As New System.Text.StringBuilder("Purchase Order: " & PONumber & Constants.vbLf)
strbuf.Append(Constants.vbTab & "Customer: " & CustomerId & Constants.vbLf)
strbuf.Append(Constants.vbTab & "OrderDetails" & Constants.vbLf)
For Each lineItem In orderLineItems
strbuf.Append(Constants.vbTab + Constants.vbTab + lineItem.ToString())
Next lineItem
strbuf.Append(Constants.vbTab & "Total cost of this order: $" & TotalCost + Constants.vbLf)
strbuf.Append(Constants.vbTab & "Order status: " & Status + Constants.vbLf)
Return strbuf.ToString()
End Function
End Class
' Order Processing Logic
' Can replace with transaction-aware resource such as SQL or transacted hashtable to hold the purchase orders.
' This example uses a non-transactional resource.
Public Class Orders
Private Shared purchaseOrders As New Dictionary(Of String, PurchaseOrder)()
Public Shared Sub Add(ByVal po As PurchaseOrder)
purchaseOrders.Add(po.PONumber, po)
End Sub
Public Shared Function GetOrderStatus(ByVal poNumber As String) As String
Dim po As PurchaseOrder = Nothing
If purchaseOrders.TryGetValue(poNumber, po) Then
Return po.Status
Else
Return Nothing
End If
End Function
Public Shared Sub DeleteOrder(ByVal poNumber As String)
If purchaseOrders(poNumber) IsNot Nothing Then
purchaseOrders.Remove(poNumber)
End If
End Sub
End Class
' Define a service contract.
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
Public Interface IOrderProcessor
<OperationContract(IsOneWay:=True)> _
Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder)
End Interface
' Service class that implements the service contract.
' Added code to write output to the console window.
Public Class OrderProcessorService
Implements IOrderProcessor
<OperationBehavior(TransactionScopeRequired:=True, TransactionAutoComplete:=True)> _
Public Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder) Implements IOrderProcessor.SubmitPurchaseOrder
Orders.Add(po)
Console.WriteLine("Processing {0} ", po)
End Sub
End Class
End Namespace
// This is the hosting application.
using System;
using System.ServiceModel.Channels;
using System.Configuration;
using System.Messaging;
using System.ServiceModel;
using System.Transactions;
using System.Runtime.Serialization;
using System.Collections.Generic;
namespace Microsoft.ServiceModel.Samples
{
class hostApp
{
// Host the service within this EXE console application.
public static void Main()
{
// Get MSMQ queue name from appsettings in configuration.
string queueName = ConfigurationManager.AppSettings["queueName"];
// Create the transacted MSMQ queue if necessary.
if (!MessageQueue.Exists(queueName))
MessageQueue.Create(queueName, true);
// Create a ServiceHost for the OrderProcessorService type.
using (ServiceHost serviceHost = new ServiceHost(typeof(OrderProcessorService)))
{
// Open the ServiceHost to create listeners and start listening for messages.
serviceHost.Open();
// The service can now be accessed.
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostB to shutdown the service.
serviceHost.Close();
}
}
}
}
' This is the hosting application.
Imports System.ServiceModel.Channels
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.Transactions
Imports System.Runtime.Serialization
Imports System.Collections.Generic
Namespace Microsoft.ServiceModel.Samples
Friend Class hostApp
' Host the service within this EXE console application.
Public Shared Sub Main()
' Get MSMQ queue name from appsettings in configuration.
Dim queueName As String = ConfigurationManager.AppSettings("queueName")
' Create the transacted MSMQ queue if necessary.
If (Not MessageQueue.Exists(queueName)) Then
MessageQueue.Create(queueName, True)
End If
' Create a ServiceHost for the OrderProcessorService type.
Using serviceHost As New ServiceHost(GetType(OrderProcessorService))
' Open the ServiceHost to create listeners and start listening for messages.
serviceHost.Open()
' The service can now be accessed.
Console.WriteLine("The service is ready.")
Console.WriteLine("Press <ENTER> to terminate service.")
Console.WriteLine()
Console.ReadLine()
' Close the ServiceHostB to shutdown the service.
serviceHost.Close()
End Using
End Sub
End Class
End Namespace
// This is the client code.
// Copyright (c) Microsoft Corporation. All Rights Reserved.
using System;
using System.Configuration;
using System.Messaging;
using System.ServiceModel;
using System.Transactions;
namespace Microsoft.ServiceModel.Samples
{
//The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool.
//Client implementation code.
class Client
{
static void Main()
{
// Create a client.
OrderProcessorClient client = new OrderProcessorClient();
// Create the purchase order.
PurchaseOrder po = new PurchaseOrder();
po.CustomerId = "somecustomer.com";
po.PONumber = Guid.NewGuid().ToString();
PurchaseOrderLineItem lineItem1 = new PurchaseOrderLineItem();
lineItem1.ProductId = "Blue Widget";
lineItem1.Quantity = 54;
lineItem1.UnitCost = 29.99F;
PurchaseOrderLineItem lineItem2 = new PurchaseOrderLineItem();
lineItem2.ProductId = "Red Widget";
lineItem2.Quantity = 890;
lineItem2.UnitCost = 45.89F;
po.orderLineItems = new PurchaseOrderLineItem[2];
po.orderLineItems[0] = lineItem1;
po.orderLineItems[1] = lineItem2;
//Create a transaction scope.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
// Make a queued call to submit the purchase order.
client.SubmitPurchaseOrder(po);
// Complete the transaction.
scope.Complete();
}
//Closing the client gracefully closes the connection and cleans up resources.
client.Close();
Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
}
}
' This is the client code.
' Copyright (c) Microsoft Corporation. All Rights Reserved.
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.Transactions
Namespace Microsoft.ServiceModel.Samples
'The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool.
'Client implementation code.
Friend Class Client
Shared Sub Main()
' Create a client.
Dim client As New OrderProcessorClient()
' Create the purchase order.
Dim po As New PurchaseOrder()
po.CustomerId = "somecustomer.com"
po.PONumber = Guid.NewGuid().ToString()
Dim lineItem1 As New PurchaseOrderLineItem()
lineItem1.ProductId = "Blue Widget"
lineItem1.Quantity = 54
lineItem1.UnitCost = 29.99F
Dim lineItem2 As New PurchaseOrderLineItem()
lineItem2.ProductId = "Red Widget"
lineItem2.Quantity = 890
lineItem2.UnitCost = 45.89F
po.orderLineItems = New PurchaseOrderLineItem(1) {}
po.orderLineItems(0) = lineItem1
po.orderLineItems(1) = lineItem2
'Create a transaction scope.
Using scope As New TransactionScope(TransactionScopeOption.Required)
' Make a queued call to submit the purchase order.
client.SubmitPurchaseOrder(po)
' Complete the transaction.
scope.Complete()
End Using
'Closing the client gracefully closes the connection and cleans up resources.
client.Close()
Console.WriteLine()
Console.WriteLine("Press <ENTER> to terminate client.")
Console.ReadLine()
End Sub
End Class
End Namespace
Consulte también
- NetMsmqBinding
- Enlace MSMQ por transacciones
- Las colas en WCF
- Procedimiento para intercambiar mensajes con puntos de conexión de WCF y aplicaciones de Message Queue Server
- Windows Communication Foundation a Message Queuing
- Instalar Message Queuing (MSMQ)
- Message Queuing a Windows Communication Foundation
- Seguridad de mensajes mediante Message Queuing