WCF Proxy object

Yes, it possible to create a proxy programmatically (and there are several different approaches, only one of which is shown here).  And it is also useful.  For example, imagine that you had a Silverlight or WPF client in a Prism environment. You want to dynamically load an assembly with MEF that contained extensions to an existing service or data contract.  Using a programmatically generated proxy, rather than an “Update Service Reference” command in Visual Studio, would greatly aid in this.

This article focuses on the simplest approach that can possibly work to programmatically generating a client proxy, so as to facilitate learning.  This is likely not the approach you would use in a real life situation, but when you understand the example presented here you’ll have no problem developing much better real life programmatically generated proxies.

Ingredients

The Server needs:

  •         Service Contract(s).
  •         Service implementation.
  •         Data Contract(s) for complex types.  Simple types like int, string, etc. do not need Data Contracts.
  •         Service Endpoint – The service Address, Binding, and Contract.
  •         Service Host – It runs the service implementation and causes the WCF runtime to create a Server-side Channel Stack to operate the Service Endpoint.  The Channel Stack communicates with the Client Proxy, and processes all messages going back and forth, including serializing/deserializing all objects identified in the Data Contracts.

The Client needs – Almost the same things as the server:

  •         Service Contract(s) – Same as Server.
  •         Data Contract(s) – Almost the same as Server.
  •         Service Endpoint – Same as Server.
  •         Proxy  – Contains all of the above, plus ServiceClient class that causes the WCF   runtime to create a Client-side Channel Stack to process all messages on the Client Side.  Using Data Contracts, the Channel Stack serializes/deserializes all objects received from/sent to the Server.

Note both the Client and Server need the same Service Contract, Data Contract(s), and Endpoints.  Indeed the Service Contract can be copied and pasted from the Server files into the Client Proxy file.  This is described below.  The information in the Endpoint is placed into the Server’s Service Host and also the Client Proxy Class, also described below

Using the Ingredients

The Visual Studio solution containing this example is in the file named WCFProxyFromScratchColorsService.zip which can be down loaded at:

https://skydrive.live.com/#cid=AE7DBEA5CE02CF22&id=AE7DBEA5CE02CF22%21105

The below screen shot of the Solution Explorer for this solution shows where the various ingredients are located.

Notice that there is no Client Service Reference.  And there are no App.config files in the Client nor Service.  This is because all the information normally in a Service Reference and App.config file is hand coded.  This code can be found in the following files.

  •         The Service Contract is located on the Server in ColorsService.ServiceInterface.cs, and on the Client in ColorsClientConsole.Proxy.cs.  They both contain exact dulicate copies of the Service Contract.
  •         The Data Contract is located on the Server in ColorsBusinessLayer.ColorsBusinessObjects.cs, and on the Client in ColorsClientConsole.Proxy.cs.  They both contain exactly the same members.  The Server-side Data Contract can use automatic properties or full properties.  However, the Client-side Data Contract must use full properties, with private backing variables.  Full properties are required by the Client deserializer.  Without them you will get null assigned to each field.
  •         The Service Host is located in ColorsServiceHostConsole.Program.cs.  This is where the WCF Channel is created and maintained that runs the service on the Server-side.  This is done with the WCF ServiceHost<T> type.
  •         The Proxy in ColorsClientConsole.Proxy.cs also contains the code that creates the Client-side WCF Channel, in addition to the Service and Data Contracts.  The Client-side WCF channel is in the class ColorsServiceClient in ColorsClientConsole.Proxy.cs.  This class uses the WCF type ChannelFactory to create the channel and allow the Client code to communicate with the Service.

Here is the code for the Client-side ColorsClientConsole.Proxy.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using System;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
namespace ColorsClientConsole
{
    ///
<summary> /// Service Contract
 /// </summary>
    [ServiceContract(Namespace = "GeorgeStevens.Software.Developer/2012/05")]
    public interface IColorsService
    {
        [OperationContract]
        string GetColorBrushString(string colorName);
        [OperationContract]
        ColorSwatch GetColor(string colorName);
    }
    ///
<summary> /// Data Contract
 /// </summary>
    [DataContract(Namespace = "GeorgeStevens.Software.Developer.Schemas/2012/05")]
    public class ColorSwatch
    {
        [DataMember]
        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }
        private int _id;
        [DataMember]
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        private string _name;
        [DataMember]
        public string Description
        {
            get { return _description; }
            set { _description = value; }
        }
        private string _description;
        [DataMember]
        public string ARGBString
        {
            get { return aRGBString; }
            set { aRGBString = value; }
        }
        private string aRGBString;
    }
    ///
<summary> /// The Client Proxy class
 /// </summary>
    public class ColorsServiceClient
    {
        public static IColorsService GetProxy()
        {
            EndpointAddress ep = new EndpointAddress("http://localhost:8732/ColorsService/ColorsServiceImpl");
            IColorsService proxy =
                ChannelFactory.CreateChannel(new BasicHttpBinding(), ep);
            return proxy;
        }
    }
}

The above file shows that the hand coded programmatic way of generating a WCF proxy is quite straight forward.  It removes a lot of the complexity from the xml and code generated by Add Service Reference so that one can see exactly what is going on.

Try placing a breakpoint on one of the setters in the Data Contract.  It will be hit when the deserializer is initializing the object’s properties.

It would be useful to compare the above code to that generated by Add Service Reference.  I’ll leave that as an exercise for the reader.  This will give you a deeper insight into what is going on in the proxy.  One thing that you will notice is a generated proxy uses ClientBase<T> rather than ChannelFactory to generate the channel.  I used ChannelFactory since its main purpose is to allow the creation of proxies programmatically, on the fly.

Here is the code for the Server-side ColorsServiceHostConsole.Program.cs.  Compare this code and its Address, Binding, and Contract with those in the ColorsServiceClient code, above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void Main(string[] args)
{
     Console.WriteLine("ColorsServiceHost Console:\n");
     ///
<summary> /// The Service Host -- Contains the Serivce's address, binding, and contract.
 /// Constructs a channel that processes messages to/from the Client and serializes/deserializes
 /// data going to/from the Client according to the DataContract defined above.
 /// </summary>
     // Args = Service type, base address(es).
     using(ServiceHost host = new ServiceHost(typeof(ColorsService.ColorsServiceImpl), new Uri"http://localhost:8732/ColorsService"))
     {
          // Args = Contract, Binding, Address (within the above base address)
          host.AddServiceEndpoint(typeof(ColorsService.IColorsService), new BasicHttpBinding(), "ColorsServiceImpl");
          host.Open();
          Console.WriteLine("\nPress ENTER to terminate this ColorsServiceHost Console.");
          Console.ReadLine();
     }
}

Download the example solution and play with it.  To run the solution:

  • First, start the Service Host.  Set the start up project to the ColorsServiceHostConsole project, then press F5.
  • Second, start the ColorsClientConsole by hovering over this project in the Solution Explorer, right clicking, the selecting Debug > Start New Instance.  This will launch the Client app which immediately launches some service operations and reports back the results to the display.

P.S.  You will have to start Visual Studio as Administrator if you are not logged in as an Admin.  Otherwise you will get a WCF exception when the Service Host starts.

A Better Way

If you want to use this technique in production software, I suggest you create a separate project in the Shared folder in the above Solution Explorer screen shot.  The project in this folder will contain WCF data and service contracts that are shared by the Clients and Services.  Perhaps call the project SharedContracts.  Then have both the Service and the Client reference the SharedContracts dll.  This way, there will be only 1 copy of all service and data contracts to maintain, rather than the duplicate copies of each that is shown in this example for the purpose of demonstration only.

source: George Stevens

Advertisements

WCF Transaction Configuration Steps

Step 1) Contact được thiết lập thuộc tính:

[ServiceContract]
public interface IService1
{
[OperationContract, TransactionFlow(TransactionFlowOption.Mandatory)]
bool PerformCreditTransaction(string creditAccountID, double amount);

Step 2)
Lớp cài đặt dịch vụ được thiết lập như sau:

[OperationBehavior(TransactionScopeRequired = true)]
public bool PerformCreditTransaction(string creditAccountID, double amount)

Step 3) cấu hình web.config

<services>
<service name=”WcfServiceTransaction.Service1″ behaviorConfiguration=”WcfServiceTransaction.Service1Behavior”>
<endpoint address=”” binding=”wsHttpBinding” contract=”WcfServiceTransaction.IService1″ bindingConfiguration=”httpBinding”>
<identity>
<dns value=”localhost”/>
</identity>
</endpoint>
<endpoint address=”mex” binding=”mexHttpBinding” contract=”IMetadataExchange”/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name=”httpBinding” transactionFlow=”true”/>
</wsHttpBinding>
</bindings>

Step 4) Client

Sử dụng TransactionScope và ts.Complete()

WCF Transaction

Introduction

In this article we will discuss about creating transaction enabled WCF service. We will see what needs to be done on the WCF service end so that it support transactions. We will also see how a client application can work with thesetransaction enabled services using a sample application.

Background

When we talk about transactions in database operations, we need to perform some operation(database operations) in such a way that either all the operations are successful or all of them fail. This would result in the amount information being same once the transaction is successful or it fails.

Properties of Transaction

By definition a transaction must be Atomic, Consistent, Isolated and Durable. What does we mean by all these terms

  • Atomic: Atomic means that all the statements (SQL statement or operations) that are a part of the transactionshould work as atomic operation i.e. either all are successful or all should fail.
  • Consistent: This means that in case the atomic transaction success, the database should be in a state that reflect changes. If the transaction fails then database should be exactly like it was when the transactionstarted.
  • Isolated: If more than one transactions are in process then each of these transactions should work independently and should not effect the other transactions.
  • Durable: Durability means that once the transaction is committed, the changes should be permanent i.e. these changes will get saved in database and should persist no matter what(like power failure or something).

Now having transactions from a simple class library’s perspective ot from a simple .Net applications, it is just a matter of calling the operations within a transaction, checking whether all the operations are successful or not and deciding whether to commit or rollback the transaction. Refer this article to know about transactions in normal application: A Beginner’s Tutorial for Understanding Transactions and TransactionScope in ADO.NET[^]

But from a WCF service perspective, since the service itself may be running on a remote server and all the communication between the service and the client is in form of messages, the service itself need some configuration so that it can be made transaction enabled.

Now in rest of the article we will see how we can configure a WCF service to support transactions and how we can call a WCF operation within transactions.

Using the code

Understanding Two Phase Commit

The WCF transactions happen using two phase commit protocol. Two phase commit protocol is the protocol that is used to enable transactions in a distributed environment. This protocol mainly consist of two phases:

  • Prepare phase: In this phase the client application performs the operations of a WCF service. WCF service determines whether the requested operation will be successful or not and notify the client about the same.
  • Commit Phase: In the commit phase the client checks for the responses it got from the prepare phase and if all the responses are indicating that the operation can be carried out successfully the transaction is committed. If the response from any one of the operations indicates failure then the transaction will be rolled back. The actual operation on the service end will happen in the commit phase.

Now from the protocol, it is pretty clear that the WCF service will have to send the notification of whether the operation will succeed or fail to the client application. It would mean that the One way operations can never supporttransactions. The operations that support transactions have to follow the Request-Response Model(refer this for details on message exchange modes: Tutorial on Message Exchange Patterns and Asynchronous Operations inWCF[^])

A note on binding

Since services communicate in form of messages the underlying message specifications play a very important role in supporting transactions. To have the possibility of transactions, the WS-AT(WS-AtomicTransaction) protocol need to be used. The binding that supports this is wsHttpBinding. So we will be using this binding to create our transaction enabled service.

Description of the Test Application

To illustrate the above process, let say I have two account holders, one person is trying to transfer some money to other person. From the database perspective this operation consist of two sub-operations i.e.

  • Debiting the first account by specified amount.
  • Secondly, crediting the second account with required amount.

Now from a technical perspective, if the first operation is successful but second one fails the result would be that the first persons account will be debited but second one will not be credited i.e. we loose the amount of information. The other way round will in fact increase the amount ion second account without even debiting the first amount.

So the bottom-line here is that we need either both of them to be successful to both of them should fail. Success of any one operation will result in inconsistent results and thus even if one operation fails we need to rollback what we did in the other operation. This is precisely where transaction are useful.

Let us say that the operations for credit and debit are exposed separately from a service. What we need to do is that, we need to transaction enable this service and then call these two methods within a transaction. The transactionwill be committed only when both the operation indicate success. In case any one operation indicates failure or throws an exception, the transaction will not be committed.

Creating the Service

Let us create a simple service with a ServiceContract that exposes operations to debit an account, credit an account and get the account balance information for any account.

 Collapse | Copy Code
[ServiceContract]
public interface IService1
{
    [OperationContract]
    bool PerformCreditTransaction(string creditAccountID, double amount);

    [OperationContract]
    bool PerformDebitTransaction(string debitAccountID, double amount);

    [OperationContract]
    decimal GetAccountDetails(int id);
}

Now we want the operations PerformCreditTransaction and PerformDebitTransaction to work within atransaction. To do this we need to make some configuration in the service. The very first thing that is required with the OperationContract is to set the TransactionFlow property with the desired TransactionFlowOption. There are 3 possible values for TransactionFlowOption:

  • Mandatory: This specifies that this function can only be called within a transaction.
  • Allowed: This specifies that this operation can be called within a transaction but its not mandatory.
  • NotAllowed: This specifies that this operation can not be called within a transaction.

Now for both our operations, we want the operations to be called mandatory within a transaction so we will specify them with this option.

 Collapse | Copy Code
[ServiceContract]
public interface IService1
{
    [OperationContract, TransactionFlow(TransactionFlowOption.Mandatory)]
    bool PerformCreditTransaction(string creditAccountID, double amount);

    [OperationContract, TransactionFlow(TransactionFlowOption.Mandatory)]
    bool PerformDebitTransaction(string debitAccountID, double amount);

    [OperationContract]
    decimal GetAccountDetails(int id);
}

Now to illustrate the implementation part, We will work on a small application that contains a single table database. This table contains the account id and the amount present in the account.

The sample DB table looks like:

The UI will look like:

And now to perform these operations, we will write simple ADO.NET code in our service implementation. The important thing from the service implementation perspective is that the service implementation also needs to be decorated/adorned with OperationBehavior attribute with TransactionScopeRequired property set to true. So now let us look at the sample implementation of the service operations.

 Collapse | Copy Code
public class Service1 : IService1
{
    readonly string CONNECTION_STRING = ConfigurationManager.ConnectionStrings["SampleDbConnectionString1"].ConnectionString;

    [OperationBehavior(TransactionScopeRequired = true)]
    public bool PerformCreditTransaction(string creditAccountID, double amount)
    {
        bool creditResult = false;

        try
        {
            using (SqlConnection con = new SqlConnection(CONNECTION_STRING))
            {
                con.Open();

                // And now do a credit
                using (SqlCommand cmd = con.CreateCommand())
                {
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = string.Format(
                        "update Account set Amount = Amount + {0} where ID = {1}",
                        amount, creditAccountID);

                    // Let us emulate some failure here to see the that transaction will not
                    // get committed
                    // return false;

                    creditResult = cmd.ExecuteNonQuery() == 1;
                }
            }
        }
        catch
        {
            throw new FaultException("Something went wring during credit");
        }
        return creditResult;
    }

    [OperationBehavior(TransactionScopeRequired = true)]
    public bool PerformDebitTransaction(string debitAccountID, double amount)
    {
        bool debitResult = false;

        try
        {
            using (SqlConnection con = new SqlConnection(CONNECTION_STRING))
            {
                con.Open();

                // Let us do a debit
                using (SqlCommand cmd = con.CreateCommand())
                {
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = string.Format(
                        "update Account set Amount = Amount - {0} where ID = {1}",
                        amount, debitAccountID);

                    debitResult = cmd.ExecuteNonQuery() == 1;
                }
            }
        }
        catch
        {
            throw new FaultException("Something went wring during debit");
        }
        return debitResult;
    }

    public decimal GetAccountDetails(int id)
    {
        decimal? result = null;

        using (SqlConnection con = new SqlConnection(CONNECTION_STRING))
        {
            using (SqlCommand cmd = con.CreateCommand())
            {
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = string.Format("select Amount from Account where ID = {0}", id);

                try
                {
                    con.Open();
                    result = cmd.ExecuteScalar() as decimal?;
                }
                catch (Exception ex)
                {
                    throw new FaultException(ex.Message);
                }
            }
        }

        if (result.HasValue)
        {
            return result.Value;
        }
        else
        {
            throw new FaultException("Unable to retrieve the amount");
        }
    }
}

Note: The code is written to elaborate the transaction enabled services only, it is not as per the coding standards i.e it is vulnerable to SQL injection. It should not be taken as code that could go in production. It is just the sample code and has a lot of scope for improvement.

Now we have our ServiceContract ready and service implementation ready. Let us now use the appropriate binding i.e. wsHttpBinding to use this service. Also, in the service configuration we need to specify that this service is transaction enabled. This can be done by setting the transactionFlow property of the binding configuration ofwsHttpBiding to true

Note: Please refer the web.config of the sample code to see how this is done.

Test Application

From our test application, we will simply call the functions within a transaction(using TransactionScope). We will check both the operations’ return value, if both of the indicates success, we will commit the transaction(by calling Complete method on TransactionScope object). If any operation fails we will rollback the transaction by not calling Complete function and simply letting the TransactionScope object go out of scope).

 Collapse | Copy Code
private void PerformTransaction(string creditAccountID, string debitAccountID, double amount)
{
    // they will be used to decide whether to commit or rollback the transaction
    bool debitResult = false;
    bool creditResult = false;

    try
    {
        using (TransactionScope ts = new TransactionScope())
        {
            using (ServiceReference1.Service1Client client = new ServiceReference1.Service1Client())
            {
                debitResult = client.PerformDebitTransaction(debitAccountID, amount);
                creditResult = client.PerformCreditTransaction(creditAccountID, amount);
            }

            if (debitResult && creditResult)
            {
                // To commit the transaction 
                ts.Complete();
            }
        }
    }
    catch
    {
        // the transaction scope will take care of rolling back
    }
}

The code currently will work fine i.e. both the methods will return true and the transaction will get committed. To check the rollback action, we have a simple return false; statement commented in our service’sPerformCreditTransaction operation, un-comment this statement to check that the transaction will not get committed.

Note: The code snippets shows the relevant code in the context. Please look at the sample code to get the full understanding.

So now we have a service that supports transactions. Before wrapping up let is look at the main operations we need in order to make a service transaction enabled.

  1. Decorate the OperationContract with required TransactionFlowOption.
  2. Decorate the operation implementation with OperationBehavior with TransactionScopeRequired as true.
  3. Use wsHttpBinding to utilize the underlying WS-AtomicTransaction protocol.
  4. Specify the transactionFlow property of the binding configuration of wsHttpBinding as true.

Source: codeproject.com