Windows Communication Foundation (WCF) では、1 つの受信側アプリケーションで処理するために関連するメッセージのセットをグループ化できるセッションが提供されます。 セッションに含まれるメッセージは、同じトランザクションの一部である必要があります。 すべてのメッセージが同じトランザクションの一部であるため、1 つのメッセージの処理に失敗した場合、セッション全体がロールバックされます。 セッションは、配信不能キューと有害キューに関して同様の動作をします。 セッション用に構成されたキューに登録されたバインディングに設定された Time to Live (TTL) プロパティは、セッション全体に適用されます。 TTL の有効期限が切れる前にセッション内の一部のメッセージのみが送信された場合、セッション全体が配信不能キューに配置されます。 同様に、セッション内のメッセージをアプリケーション キューからアプリケーションに送信できない場合、セッション全体が有害キューに配置されます (使用可能な場合)。
メッセージグループ化の例
メッセージのグループ化が役立つ 1 つの例は、注文処理アプリケーションを WCF サービスとして実装する場合です。 たとえば、クライアントは、多数のアイテムを含む注文をこのアプリケーションに送信します。 各項目について、クライアントはサービスを呼び出します。これにより、個別のメッセージが送信されます。 サービス A が最初の項目を受け取り、サーバー B が 2 番目の項目を受け取ることができる。 項目が追加されるたびに、その項目を処理するサーバーは、適切な順序を見つけて項目を追加する必要があります。これは非常に非効率的です。 サーバーは現在処理中のすべての注文を追跡し、新しい項目が属している注文を特定する必要があるため、1 つのサーバーですべての要求を処理するだけで、このような非効率性が引き続き発生します。 1 つの順序のすべての要求をグループ化すると、このようなアプリケーションの実装が大幅に簡略化されます。 クライアント アプリケーションは、セッション内の 1 つの注文のすべての項目を送信するため、サービスが注文を処理すると、セッション全体が一度に処理されます。 \
プロシージャ
セッションを使用するようにサービス コントラクトを設定するには
セッションを必要とするサービス コントラクトを定義します。 次のように指定して、 ServiceContractAttribute 属性でこれを行います。
SessionMode=SessionMode.Required
これらのメソッドは何も返さないため、コントラクト内の操作を一方向としてマークします。 これを行うには、 OperationContractAttribute 属性で次を指定します。
[OperationContract(IsOneWay = true)]
サービス コントラクトを実装し、InstanceContextMode.PerSessionのInstanceContextModeを指定します。 これにより、サービスはセッションごとに 1 回だけインスタンス化されます。
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
各サービス操作にはトランザクションが必要です。 これを OperationBehaviorAttribute 属性で指定します。 トランザクションを完了する操作も、 TransactionAutoComplete を
true
に設定する必要があります。[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
システム提供の
NetMsmqBinding
バインディングを使用するエンドポイントを構成します。System.Messagingを使用してトランザクション キューを作成します。 メッセージ キュー (MSMQ) または MMC を使用してキューを作成することもできます。 その場合は、トランザクション キューを作成します。
ServiceHostを使用して、サービスのサービス ホストを作成します。
サービス ホストを開いて、サービスを使用可能にします。
サービス ホストを閉じます。
クライアントを設定するには
トランザクション キューに書き込むトランザクション スコープを作成します。
ServiceModel メタデータ ユーティリティ ツール (Svcutil.exe) ツールを使用して WCF クライアントを作成します。
注文を行います。
WCF クライアントを閉じます。
例
説明
次の例では、 IProcessOrder
サービスと、このサービスを使用するクライアントのコードを示します。 WCF がキューに登録されたセッションを使用してグループ化の動作を提供する方法を示します。
サービスのコード
// Service Code:
using System;
using System.ServiceModel.Channels;
using System.Configuration;
using System.Messaging;
using System.ServiceModel;
using System.Transactions;
using System.Text;
using System.Collections.Generic;
namespace Microsoft.ServiceModel.Samples
{
// Define a service contract.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)]
public interface IOrderTaker
{
[OperationContract(IsOneWay = true)]
void OpenPurchaseOrder(string customerId);
[OperationContract(IsOneWay = true)]
void AddProductLineItem(string productId, int quantity);
[OperationContract(IsOneWay = true)]
void EndPurchaseOrder();
}
// Define the Purchase Order Line Item
public class PurchaseOrderLineItem
{
static Random r = new Random(137);
string ProductId;
float UnitCost;
int Quantity;
public PurchaseOrderLineItem(string productId, int quantity)
{
this.ProductId = productId;
this.Quantity = quantity;
this.UnitCost = r.Next(10000);
}
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 Purchase Order
public class PurchaseOrder
{
string PONumber;
string CustomerId;
LinkedList<PurchaseOrderLineItem> orderLineItems = new LinkedList<PurchaseOrderLineItem>();
public PurchaseOrder(string customerId)
{
this.CustomerId = customerId;
this.PONumber = Guid.NewGuid().ToString();
}
public void AddProductLineItem(string productId, int quantity)
{
orderLineItems.AddLast(new PurchaseOrderLineItem(productId, quantity));
}
public float TotalCost
{
get
{
float totalCost = 0;
foreach (PurchaseOrderLineItem lineItem in orderLineItems)
totalCost += lineItem.TotalCost;
return totalCost;
}
}
public string Status
{
get
{
return "Pending";
}
}
public override string ToString()
{
StringBuilder strbuf = new 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();
}
}
// Service class which implements the service contract.
// Added code to write output to the console window
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class OrderTakerService : IOrderTaker
{
PurchaseOrder po;
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public void OpenPurchaseOrder(string customerId)
{
Console.WriteLine("Creating purchase order");
po = new PurchaseOrder(customerId);
}
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public void AddProductLineItem(string productId, int quantity)
{
po.AddProductLineItem(productId, quantity);
Console.WriteLine("Product " + productId + " quantity " + quantity + " added to purchase order");
}
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void EndPurchaseOrder()
{
Console.WriteLine("Purchase Order Completed");
Console.WriteLine();
Console.WriteLine(po.ToString());
}
// Host the service within this EXE console application.
public static void Main()
{
// Get MSMQ queue name from app settings in configuration
string queueName = ConfigurationManager.AppSettings["queueName"];
// Create the transacted MSMQ queue if necessary.
if (!MessageQueue.Exists(queueName))
MessageQueue.Create(queueName, true);
// Get the base address that is used to listen for WS-MetaDataExchange requests
string baseAddress = ConfigurationManager.AppSettings["baseAddress"];
// Create a ServiceHost for the OrderTakerService type.
using (ServiceHost serviceHost = new ServiceHost(typeof(OrderTakerService), new Uri(baseAddress)))
{
// Open the ServiceHostBase 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 ServiceHostBase to shutdown the service.
serviceHost.Close();
}
}
}
}
' Service Code:
Imports System.ServiceModel.Channels
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.Transactions
Imports System.Text
Imports System.Collections.Generic
Namespace Microsoft.ServiceModel.Samples
' Define a service contract.
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples", SessionMode:=SessionMode.Required)> _
Public Interface IOrderTaker
<OperationContract(IsOneWay:=True)> _
Sub OpenPurchaseOrder(ByVal customerId As String)
<OperationContract(IsOneWay:=True)> _
Sub AddProductLineItem(ByVal productId As String, ByVal quantity As Integer)
<OperationContract(IsOneWay:=True)> _
Sub EndPurchaseOrder()
End Interface
' Define the Purchase Order Line Item
Public Class PurchaseOrderLineItem
Private Shared r As New Random(137)
Private ProductId As String
Private UnitCost As Single
Private Quantity As Integer
Public Sub New(ByVal productId As String, ByVal quantity As Integer)
Me.ProductId = productId
Me.Quantity = quantity
Me.UnitCost = r.Next(10000)
End Sub
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 Purchase Order
Public Class PurchaseOrder
Private PONumber As String
Private CustomerId As String
Private orderLineItems As New LinkedList(Of PurchaseOrderLineItem)()
Public Sub New(ByVal customerId As String)
Me.CustomerId = customerId
Me.PONumber = Guid.NewGuid().ToString()
End Sub
Public Sub AddProductLineItem(ByVal productId As String, ByVal quantity As Integer)
orderLineItems.AddLast(New PurchaseOrderLineItem(productId, quantity))
End Sub
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 "Pending"
End Get
End Property
Public Overrides Function ToString() As String
Dim strbuf As New 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
' Service class which implements the service contract.
' Added code to write output to the console window
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)> _
Public Class OrderTakerService
Implements IOrderTaker
Private po As PurchaseOrder
<OperationBehavior(TransactionScopeRequired:=True, TransactionAutoComplete:=False)> _
Public Sub OpenPurchaseOrder(ByVal customerId As String) Implements IOrderTaker.OpenPurchaseOrder
Console.WriteLine("Creating purchase order")
po = New PurchaseOrder(customerId)
End Sub
<OperationBehavior(TransactionScopeRequired:=True, TransactionAutoComplete:=False)> _
Public Sub AddProductLineItem(ByVal productId As String, ByVal quantity As Integer) Implements IOrderTaker.AddProductLineItem
po.AddProductLineItem(productId, quantity)
Console.WriteLine("Product " & productId & " quantity " & quantity & " added to purchase order")
End Sub
<OperationBehavior(TransactionScopeRequired:=True, TransactionAutoComplete:=True)> _
Public Sub EndPurchaseOrder() Implements IOrderTaker.EndPurchaseOrder
Console.WriteLine("Purchase Order Completed")
Console.WriteLine()
Console.WriteLine(po.ToString())
End Sub
' Host the service within this EXE console application.
Public Shared Sub Main()
' Get MSMQ queue name from app settings 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
' Get the base address that is used to listen for WS-MetaDataExchange requests
Dim baseAddress As String = ConfigurationManager.AppSettings("baseAddress")
' Create a ServiceHost for the OrderTakerService type.
Using serviceHost As New ServiceHost(GetType(OrderTakerService), New Uri(baseAddress))
' Open the ServiceHostBase 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 ServiceHostBase to shutdown the service.
serviceHost.Close()
End Using
End Sub
End Class
End Namespace
クライアントのコード
using System;
using System.Configuration;
using System.Messaging;
using System.ServiceModel;
using System.Transactions;
namespace Microsoft.ServiceModel.Samples
{
//The service contract is defined in generatedProxy.cs, generated from the service by the svcutil tool.
//Client implementation code.
class Client
{
static void Main()
{
//Create a transaction scope.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
// Create a proxy with given client endpoint configuration
OrderTakerClient client = new OrderTakerClient("OrderTakerEndpoint");
try
{
// Open a purchase order
client.OpenPurchaseOrder("somecustomer.com");
Console.WriteLine("Purchase Order created");
// Add product line items
Console.WriteLine("Adding 10 quantities of blue widget");
client.AddProductLineItem("Blue Widget", 10);
Console.WriteLine("Adding 23 quantities of red widget");
client.AddProductLineItem("Red Widget", 23);
// Close the purchase order
Console.WriteLine("Closing the purchase order");
client.EndPurchaseOrder();
client.Close();
}
catch (CommunicationException ex)
{
client.Abort();
}
// Complete the transaction.
scope.Complete();
}
Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
}
}
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.Transactions
Namespace Microsoft.ServiceModel.Samples
'The service contract is defined in generatedProxy.cs, generated from the service by the svcutil tool.
'Client implementation code.
Friend Class Client
Shared Sub Main()
'Create a transaction scope.
Using scope As New TransactionScope(TransactionScopeOption.Required)
' Create a proxy with given client endpoint configuration
Dim client As New OrderTakerClient("OrderTakerEndpoint")
Try
' Open a purchase order
client.OpenPurchaseOrder("somecustomer.com")
Console.WriteLine("Purchase Order created")
' Add product line items
Console.WriteLine("Adding 10 quantities of blue widget")
client.AddProductLineItem("Blue Widget", 10)
Console.WriteLine("Adding 23 quantities of red widget")
client.AddProductLineItem("Red Widget", 23)
' Close the purchase order
Console.WriteLine("Closing the purchase order")
client.EndPurchaseOrder()
client.Close()
Catch ex As CommunicationException
client.Abort()
End Try
' Complete the transaction.
scope.Complete()
End Using
Console.WriteLine()
Console.WriteLine("Press <ENTER> to terminate client.")
Console.ReadLine()
End Sub
End Class
End Namespace