





















































(Read more interesting articles on WCF 4.0 here.)
In this article, we will create a new solution based on the LINQNorthwind solution.We will copy all of the source code from the LINQNorthwind directory to a new directory and then customize it to suit our needs. The steps here are very similar to the steps in the previous chapter when we created the LINQNorthwind solution.Please refer to the previous chapter for diagrams.
Follow these steps to create the new solution:
Now we have the file structures ready for the new solution but all the file contents and the solution structure are still for the old solution. Next we need to change them to work for the new solution.
We will first change all the related WCF service files. Once we have the service up and running we will create a new client to test this new service.
The screenshot below shows the final structure of the new solution, DistNorthwind:
Now we have finished modifying the service projects. If you build the solution now you should see no errors. You can set the service project as the startup project, run the program.
The WCF service is now hosted within WCF Service Host.We had to start the WCF Service Host before we ran our test client.Not only do you have to start the WCF Service Host, you also have to start the WCF Test client and leave it open. This is not that nice. In addition, we will add another service later in this articleto test distributed transaction support with two databases and it is not that easy to host two services with one WCF Service Host.So, in this article, we will first decouple our WCF service from Visual Studio to host it in IIS.
You can follow these steps to host this WCF service in IIS:
<%@ServiceHost Service="MyWCFServices.DistNorthwindService.
ProductService"%>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/
Design_Time_Addresses/MyWCFServices/
DistNorthwindService/ProductService/" />
</baseAddresses>
</host>
Now you have finished setting up the service to be hosted in IIS. Open Internet Explorer, go to the following address, and you should see the ProductService description in the browser: http://localhost/DistNorthwindService/ProductService.svc
Before explaining how to enhance this WCF service to support distributed transactions, we will first confirm that the existing WCF service doesn't support distributed transactions. In this article, we will test the following scenarios:
The first scenario to test is that within one method of the client application two service calls will be made and one of them will fail. We then verify whether the update in the successful service call has been committed to the database. If it has been, it will mean that the two service calls are not within a single atomic transaction and will indicate that the WCF service doesn't support distributed transactions.
You can follow these steps to create a WPF client for this test case:
Now the new test client should have been created and added to the solution. Let's follow these steps to customize this client so that we can call ProductService twice within one method and test the distributed transaction support of this WCF service:
private string GetProduct(TextBox txtProductID, ref Product
product)
{
string result = "";
try
{
int productID = Int32.Parse(txtProductID.Text.ToString());
ProductServiceClient client = new ProductServiceClient();
product = client.GetProduct(productID);
StringBuilder sb = new StringBuilder();
sb.Append("ProductID:" +
product.ProductID.ToString() + "n");
sb.Append("ProductName:" +
product.ProductName + "n");
sb.Append("UnitPrice:" +
product.UnitPrice.ToString() + "n");
sb.Append("RowVersion:");
foreach (var x in product.RowVersion.AsEnumerable())
{
sb.Append(x.ToString());
sb.Append(" ");
}
result = sb.ToString();
}
catch (Exception ex)
{
result = "Exception: " + ex.Message.ToString();
}
return result;
}
This method will call the product service to retrieve a product from the database, format the product details to a string, and return the string. This string will be displayed on the screen. The product object will also be returned so that later on we can reuse this object to update the price of the product.
Now we have finished adding code to retrieve products from the database through the Product WCF service. Set DistNorthwindWPF as the startup project, press Ctrl + F5 to start the WPF test client, enter 30 and 31 as the product IDs, and then click on the Get Product Details button. You should get a window like this image:
To update the prices of these two products follow these steps to add the code to the project:
private string UpdatePrice(
TextBox txtNewPrice,
ref Product product,
ref bool updateResult)
{
string result = "";
try
{
product.UnitPrice =
Decimal.Parse(txtNewPrice.Text.ToString());
ProductServiceClient client =
new ProductServiceClient();
updateResult =
client.UpdateProduct(ref product);
StringBuilder sb = new StringBuilder();
if (updateResult == true)
{
sb.Append("Price updated to ");
sb.Append(txtNewPrice.Text.ToString());
sb.Append("n");
sb.Append("Update result:");
sb.Append(updateResult.ToString());
sb.Append("n");
sb.Append("New RowVersion:");
}
else
{
sb.Append("Price not updated to ");
sb.Append(txtNewPrice.Text.ToString());
sb.Append("n");
sb.Append("Update result:");
sb.Append(updateResult.ToString());
sb.Append("n");
sb.Append("Old RowVersion:");
}
foreach (var x in product.RowVersion.AsEnumerable())
{
sb.Append(x.ToString());
sb.Append(" ");
}
result = sb.ToString();
}
catch (Exception ex)
{
result = "Exception: " + ex.Message;
}
return result;
}
This method will call the product service to update the price of a product in the database. The update result will be formatted and returned so that later on we can display it. The updated product object with the new RowVersion will also be returned so that later on we can update the price of the same product again and again.
if (product1 == null)
{
txtUpdate1Results.Text = "Get product details first";
}
else if (product2 == null)
{
txtUpdate2Results.Text = "Get product details first";
}
else
{
bool update1Result = false, update2Result = false;
txtUpdate1Results.Text = UpdatePrice(
txtNewPrice1, ref product1, ref update1Result);
txtUpdate2Results.Text = UpdatePrice(
txtNewPrice2, ref product2, ref update2Result);
}
Let's run the program now to test the distributed transaction support of the WCF service. We will first update two products with two valid prices to make sure our code works with normal use cases. Then we will update one product with a valid price and another with an invalid price. We will verify that the update with the valid price has been committed to the database, regardless of the failure of the other update.
Let's follow these steps for this test:
We know that the second service call should fail so the second update should not be committed to the database. From the test result we know this is true (the second product price didn't change). However from the test result we also know that the first update in the first service call has been committed to the database (the first product price has been changed). This means that the first call to the service is not rolled back even when a subsequent service call has failed. Therefore each service call is in a separate standalone transaction. In other words, the two sequential service calls are not within one distributed transaction.