How to: Manage Object Services Transactions (Entity Framework)
This topic provides an example of how to define a transaction that coordinates making changes to objects in an object context with other external operations. For more information, see Managing Transactions in Object Services (Entity Framework).
The example in this topic is based on the AdventureWorks Sales Model (EDM). To run the code in this example, you must have already added the AdventureWorks Sales Model to your project and configured your project to use the Entity Framework. To do this, complete the procedure in How to: Use the Entity Data Model Wizard (Entity Framework). You must also have Microsoft Windows Message Queuing installed.
Example
This example defines a TransactionScope. The TransactionScope ensures that changes to objects in the object context are coordinated with a message queue. Object Services uses this transaction when it saves changes to the database. When an UpdateException occurs, the operation is retried up to two times. When the operation succeeds, the changes in the object context are accepted. For more information, see Managing Transactions in Object Services (Entity Framework).
This example uses a long-running object context, which is disposed after the transaction succeeds or after all retries have been attempted.
Option Explicit On
Option Strict On
Imports System
Imports System.Linq
Imports System.Data
Imports System.Data.Objects
Imports System.Messaging
Imports System.Transactions
' Prepend with the root namespace for the project.
Public Class TranasctionSample
Public Shared Sub EnlistTransaction()
Dim retries As Integer = 3
Dim queueName As String = ".\Fulfilling"
' Define variables that we need to add an item.
Dim quantity As Short = 2
Dim productId As Integer = 750
Dim orderId As Integer = 43680
' Define a long-running object context.
Dim context As AdventureWorksEntities _
= New AdventureWorksEntities()
Dim success As Boolean = False
' Wrap the operation in a retry loop.
Dim i As Integer
For i = 0 To retries - 1 Step i + 1
' Define a transaction scope for the operations.
Using transaction As New TransactionScope()
Try
' Define a query that returns a order by order ID.
Dim order As SalesOrderHeader = _
context.SalesOrderHeader.Where( _
"it.SalesOrderID = @id", New ObjectParameter( _
"id", orderId)).First()
' Load items for the order, if not already loaded.
If Not order.SalesOrderDetail.IsLoaded Then
order.SalesOrderDetail.Load()
End If
' Load the customer, if not already loaded.
If Not order.ContactReference.IsLoaded Then
order.ContactReference.Load()
End If
' Create a new item for an existing order.
Dim newItem As SalesOrderDetail = _
SalesOrderDetail.CreateSalesOrderDetail( _
0, 0, quantity, productId, 1, 0, 0, 0, Guid.NewGuid(), DateAndTime.Today)
'Add new item to the order.
order.SalesOrderDetail.Add(newItem)
' Save changes pessimistically. This means that changes
' must be accepted manually once the transaction succeeds.
context.SaveChanges(False)
' Create the message queue if it does not already exist.
If Not MessageQueue.Exists(queueName) Then
MessageQueue.Create(queueName)
End If
' Initiate fulfilling order by sending a message.
Using q As New MessageQueue(queueName)
Dim msg As New System.Messaging.Message( _
String.Format("<order customerId='{0}'>" _
+ "<orderLine product='{1}' quantity='{2}' />" _
+ "</order>", order.Contact.ContactID, _
newItem.ProductID, newItem.OrderQty))
q.Send(msg)
End Using
' Mark the transaction as complete.
transaction.Complete()
success = True
Exit For
Catch ex As Exception
' Handle errors and deadlocks here and retry if needed.
' Allow an UpdateException to pass through and
' retry, otherwise stop the execution.
If Not TypeOf (ex) Is UpdateException Then
Console.WriteLine("An error occured. " _
+ "The operation cannot be retried." _
+ ex.Message)
Exit Sub
End If
' If we get to this point, the operation will be retried.
End Try
End Using
Next
If success Then
' Reset the context since the operation succeeded.
context.AcceptAllChanges()
Else
Console.WriteLine("The operation could not be completed in " _
+ retries.ToString + " tries.")
End If
' Dispose the object context.
context.Dispose()
End Sub
End Class
using System;
using System.Linq;
using System.Data;
using System.Data.Objects;
using System.Messaging;
using System.Transactions;
namespace Microsoft.Samples.Edm
{
class TransactionSample
{
public static void EnlistTransaction()
{
int retries = 3;
string queueName = @".\Fulfilling";
// Define variables that we need to add an item.
short quantity = 2;
int productId = 750;
int orderId = 43680;
// Define a long-running object context.
AdventureWorksEntities context
= new AdventureWorksEntities();
bool success = false;
// Wrap the operation in a retry loop.
for (int i = 0; i < retries; i++)
{
// Define a transaction scope for the operations.
using (TransactionScope transaction = new TransactionScope())
{
try
{
// Define a query that returns a order by order ID.
SalesOrderHeader order =
context.SalesOrderHeader.Where
("it.SalesOrderID = @id", new ObjectParameter(
"id", orderId)).First();
// Load items for the order, if not already loaded.
if (!order.SalesOrderDetail.IsLoaded)
{
order.SalesOrderDetail.Load();
}
// Load the customer, if not already loaded.
if (!order.ContactReference.IsLoaded)
{
order.ContactReference.Load();
}
// Create a new item for an existing order.
SalesOrderDetail newItem = SalesOrderDetail.CreateSalesOrderDetail(
0, 0, quantity, productId, 1, 0, 0, 0, Guid.NewGuid(), DateTime.Today);
// Add new item to the order.
order.SalesOrderDetail.Add(newItem);
// Save changes pessimistically. This means that changes
// must be accepted manually once the transaction succeeds.
context.SaveChanges(false);
// Create the message queue if it does not already exist.
if (!MessageQueue.Exists(queueName))
{
MessageQueue.Create(queueName);
}
// Initiate fulfilling order by sending a message.
using (MessageQueue q = new MessageQueue(queueName))
{
System.Messaging.Message msg =
new System.Messaging.Message(String.Format(
"<order customerId='{0}'>" +
"<orderLine product='{1}' quantity='{2}' />" +
"</order>", order.Contact.ContactID,
newItem.ProductID, newItem.OrderQty));
// Send the message to the queue.
q.Send(msg);
}
// Mark the transaction as complete.
transaction.Complete();
success = true;
break;
}
catch (Exception ex)
{
// Handle errors and deadlocks here and retry if needed.
// Allow an UpdateException to pass through and
// retry, otherwise stop the execution.
if (ex.GetType() != typeof(UpdateException))
{
Console.WriteLine("An error occured. "
+ "The operation cannot be retried."
+ ex.Message);
break;
}
// If we get to this point, the operation will be retried.
}
}
}
if (success)
{
// Reset the context since the operation succeeded.
context.AcceptAllChanges();
}
else
{
Console.WriteLine("The operation could not be completed in "
+ retries + " tries.");
}
// Dispose the object context.
context.Dispose();
}
}
}
See Also
Other Resources
How to: Manage EntityClient Transactions (Entity Framework)
How to: Explicitly Manage EntityClient Transactions Using EntityTransaction (Entity Framework)
Managing Connections and Transactions (Entity Framework Tasks)