Correlação de mensagens
O exemplo MessageCorrelation demonstra como um aplicativo de enfileiramento de mensagens (MSMQ) pode enviar uma mensagem MSMQ para um serviço WCF (Windows Communication Foundation) e como as mensagens podem ser correlacionadas entre aplicativos remetente e recetor em um cenário de solicitação/resposta. Este exemplo usa a associação msmqIntegrationBinding. O serviço, neste caso, é um aplicativo de console auto-hospedado para permitir que você observe o serviço que recebe mensagens em fila. mil
O serviço processa a mensagem recebida do remetente e envia uma mensagem de resposta de volta ao remetente. O remetente correlaciona a resposta recebida com a solicitação enviada originalmente. As MessageID
propriedades e CorrelationID
da mensagem são usadas para correlacionar as mensagens de solicitação e resposta.
O IOrderProcessor
contrato de serviço define uma operação de serviço unidirecional que é adequada para uso com filas. Uma mensagem MSMQ não tem um cabeçalho Action, portanto, não é possível mapear mensagens MSMQ diferentes para contratos de operação automaticamente. Portanto, só pode haver um contrato de operação neste caso. Se você quiser definir mais contratos de operação no serviço, o aplicativo deve fornecer informações sobre qual cabeçalho na mensagem MSMQ (por exemplo, o rótulo ou correlationID) pode ser usado para decidir qual contrato de operação despachar.
A mensagem MSMQ também não contém informações sobre quais cabeçalhos são mapeados para os diferentes parâmetros do contrato de operação. Portanto, pode haver apenas um parâmetro no contrato de operação. O parâmetro é do tipo MsmqMessage<T>, que contém a mensagem MSMQ subjacente. O tipo "T" na MsmqMessage<T>
classe representa os dados que são serializados no corpo da mensagem MSMQ. Neste exemplo, o PurchaseOrder
tipo é serializado no corpo da mensagem MSMQ.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
[ServiceKnownType(typeof(PurchaseOrder))]
public interface IOrderProcessor
{
[OperationContract(IsOneWay = true, Action = "*")]
void SubmitPurchaseOrder(MsmqMessage<PurchaseOrder> msg);
}
A operação de serviço processa a ordem de compra e exibe o conteúdo da ordem de compra e seu status na janela do console de serviço. O OperationBehaviorAttribute configura a operação para se alistar em uma transação com a fila e marcar a transação concluída quando a operação retorna. O PurchaseOrder
contém os detalhes do pedido que devem ser processados pelo serviço.
// Service class that implements the service contract.
public class OrderProcessorService : IOrderProcessor
{
[OperationBehavior(TransactionScopeRequired = true,
TransactionAutoComplete = true)]
public void SubmitPurchaseOrder(MsmqMessage<PurchaseOrder> ordermsg)
{
PurchaseOrder po = (PurchaseOrder)ordermsg.Body;
Random statusIndexer = new Random();
po.Status = PurchaseOrder.OrderStates[statusIndexer.Next(3)];
Console.WriteLine("Processing {0} ", po);
//Send a response to the client that the order has been received
// and is pending fulfillment.
SendResponse(ordermsg);
}
private void SendResponse(MsmqMessage<PurchaseOrder> ordermsg)
{
OrderResponseClient client = new OrderResponseClient("OrderResponseEndpoint");
//Set the correlation ID such that the client can correlate the response to the order.
MsmqMessage<PurchaseOrder> orderResponseMsg = new MsmqMessage<PurchaseOrder>(ordermsg.Body);
orderResponseMsg.CorrelationId = ordermsg.Id;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
client.SendOrderResponse(orderResponseMsg);
scope.Complete();
}
client.Close();
}
}
O serviço usa um cliente OrderResponseClient
personalizado para enviar a mensagem MSMQ para a fila. Como o aplicativo que recebe e processa a mensagem é um aplicativo MSMQ e não um aplicativo WCF, não há nenhum contrato de serviço implícito entre os dois aplicativos. Portanto, não podemos criar um proxy usando a ferramenta Svcutil.exe neste cenário.
O proxy personalizado é essencialmente o mesmo para todos os aplicativos WCF que usam a msmqIntegrationBinding
associação para enviar mensagens. Ao contrário de outros proxies, ele não inclui uma gama de operações de serviço. É apenas uma operação de envio de mensagem.
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IOrderResponse
{
[System.ServiceModel.OperationContractAttribute(IsOneWay = true, Action = "*")]
void SendOrderResponse(MsmqMessage<PurchaseOrder> msg);
}
public partial class OrderResponseClient : System.ServiceModel.ClientBase<IOrderResponse>, IOrderResponse
{
public OrderResponseClient()
{ }
public OrderResponseClient(string configurationName)
: base(configurationName)
{ }
public OrderResponseClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress address)
: base(binding, address)
{ }
public void SendOrderResponse(MsmqMessage<PurchaseOrder> msg)
{
base.Channel.SendOrderResponse(msg);
}
}
O serviço é auto-hospedado. Ao usar o transporte de integração MSMQ, a fila usada deve ser criada com antecedência. Isso pode ser feito manualmente ou através de código. Neste exemplo, o serviço contém System.Messaging código para verificar a existência da fila e criá-la, se necessário. O nome da fila é lido a partir do arquivo de configuração.
public static void Main()
{
// Get the MSMQ queue name from application settings in configuration.
string queueName =
ConfigurationManager.AppSettings["orderQueueName"];
// 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)))
{
serviceHost.Open();
// The service can now be accessed.
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.ReadLine();
// Close the ServiceHost to shutdown the service.
serviceHost.Close();
}
}
A fila MSMQ para a qual as solicitações de ordem são enviadas é especificada na seção appSettings do arquivo de configuração. Os pontos de extremidade do cliente e do serviço são definidos na seção system.serviceModel do arquivo de configuração. Ambos especificam a msmqIntegrationBinding
ligação.
<appSettings>
<add key="orderQueueName" value=".\private$\Orders" />
</appSettings>
<system.serviceModel>
<client>
<endpoint name="OrderResponseEndpoint"
address="msmq.formatname:DIRECT=OS:.\private$\OrderResponse"
binding="msmqIntegrationBinding"
bindingConfiguration="OrderProcessorBinding"
contract="Microsoft.ServiceModel.Samples.IOrderResponse">
</endpoint>
</client>
<services>
<service
name="Microsoft.ServiceModel.Samples.OrderProcessorService">
<endpoint address="msmq.formatname:DIRECT=OS:.\private$\Orders"
binding="msmqIntegrationBinding"
bindingConfiguration="OrderProcessorBinding"
contract="Microsoft.ServiceModel.Samples.IOrderProcessor">
</endpoint>
</service>
</services>
<bindings>
<msmqIntegrationBinding>
<binding name="OrderProcessorBinding" >
<security mode="None" />
</binding>
</msmqIntegrationBinding>
</bindings>
</system.serviceModel>
O aplicativo cliente usa System.Messaging para enviar uma mensagem durável e transacional para a fila. O corpo da mensagem contém a ordem de compra.
static void PlaceOrder()
{
//Connect to the queue
MessageQueue orderQueue =
new MessageQueue(
ConfigurationManager.AppSettings["orderQueueName"])
// 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;
Message msg = new Message();
msg.UseDeadLetterQueue = true;
msg.Body = po;
//Create a transaction scope.
using (TransactionScope scope = new
TransactionScope(TransactionScopeOption.Required))
{
// Submit the purchase order.
orderQueue.Send(msg, MessageQueueTransactionType.Automatic);
// Complete the transaction.
scope.Complete();
}
//Save the messageID for order response correlation.
orderMessageID = msg.Id;
Console.WriteLine("Placed the order, waiting for response...");
}
A fila MSMQ da qual as respostas do pedido são recebidas é especificada em uma seção appSettings do arquivo de configuração, conforme mostrado na configuração de exemplo a seguir.
Nota
O nome da fila usa um ponto (.) para o computador local e separadores de barra invertida em seu caminho. O endereço do ponto de extremidade WCF especifica um esquema msmq.formatname e usa "localhost" para o computador local. Um nome de formato formado corretamente segue msmq.formatname no URI de acordo com as diretrizes do MSMQ.
<appSettings>
<add key=" orderResponseQueueName" value=".\private$\Orders" />
</appSettings>
O aplicativo cliente salva a messageID
mensagem de solicitação de pedido que envia ao serviço e aguarda uma resposta do serviço. Quando uma resposta chega na fila, o cliente a correlaciona com a mensagem de pedido enviada usando a correlationID
propriedade da mensagem, que contém a messageID
mensagem de ordem que o cliente enviou para o serviço originalmente.
static void DisplayOrderStatus()
{
MessageQueue orderResponseQueue = new
MessageQueue(ConfigurationManager.AppSettings
["orderResponseQueueName"]);
//Create a transaction scope.
bool responseReceived = false;
orderResponseQueue.MessageReadPropertyFilter.CorrelationId = true;
while (!responseReceived)
{
Message responseMsg;
using (TransactionScope scope2 = new
TransactionScope(TransactionScopeOption.Required))
{
//Receive the Order Response message.
responseMsg =
orderResponseQueue.Receive
(MessageQueueTransactionType.Automatic);
scope2.Complete();
}
responseMsg.Formatter = new
System.Messaging.XmlMessageFormatter(new Type[] {
typeof(PurchaseOrder) });
PurchaseOrder responsepo = (PurchaseOrder)responseMsg.Body;
//Check if the response is for the order placed.
if (orderMessageID == responseMsg.CorrelationId)
{
responseReceived = true;
Console.WriteLine("Status of current Order: OrderID-{0},Order
Status-{1}", responsepo.PONumber, responsepo.Status);
}
else
{
Console.WriteLine("Status of previous Order: OrderID-{0},Order
Status-{1}", responsepo.PONumber, responsepo.Status);
}
}
}
Quando você executa o exemplo, as atividades do cliente e do serviço são exibidas nas janelas do console do serviço e do cliente. Você pode ver o serviço receber mensagens do cliente e enviar uma resposta de volta para o cliente. O cliente exibe a resposta recebida do serviço. Pressione ENTER em cada janela do console para desligar o serviço e o cliente.
Nota
Este exemplo requer a instalação do serviço de enfileiramento de mensagens (MSMQ). Consulte as instruções de instalação do MSMQ na seção Consulte também.
Configurar, compilar e executar o exemplo
Certifique-se de ter executado o procedimento de instalação única para os exemplos do Windows Communication Foundation.
Se o serviço for executado primeiro, ele verificará se a fila está presente. Se a fila não estiver presente, o serviço criará uma. Você pode executar o serviço primeiro para criar a fila ou pode criar uma por meio do Gerenciador de Filas MSMQ. Siga estas etapas para criar uma fila no Windows 2008.
Abra o Gerenciador do Servidor no Visual Studio 2012.
Expanda a guia Recursos .
Clique com o botão direito do mouse em Filas de Mensagens Privadas e selecione Nova, Fila Privada.
Marque a caixa Transacional .
Digite
ServiceModelSamplesTransacted
como o nome da nova fila.
Para criar a edição C# ou Visual Basic .NET da solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.
Para executar o exemplo em uma configuração de computador único, siga as instruções em Executando os exemplos do Windows Communication Foundation.
Execute o exemplo em todos os computadores
Copie os arquivos do programa de serviço da pasta \service\bin\, na pasta específica do idioma, para o computador de serviço.
Copie os arquivos de programa cliente da pasta \client\bin\, na pasta específica do idioma, para o computador cliente.
No arquivo Client.exe.config, altere orderQueueName para especificar o nome do computador de serviço em vez de ".".
No arquivo Service.exe.config, altere o endereço do ponto de extremidade do cliente para especificar o nome do computador cliente em vez de ".".
No computador de serviço, inicie Service.exe a partir de um prompt de comando.
No computador cliente, inicie Client.exe a partir de um prompt de comando.