Adding Transactions to the Solution






Adding Transactions to the Solution

In this exercise, you will create a solution that consists of an ATM Client, a Web Service for a Bank, and a Bill Payment Web Service for the Electric Company.

In this scenario you will enable customers to pay their electric bill through the ATM. The ATM will connect to the Banking Web Service, which in turn creates a transaction when connecting to the Electric Company.

In this example the Banking Service is a client to a service at the Electric Company, and the transaction occurs between the Bank and the Electric Company.

Creating the Electric Company Service

The electric company would like to be able to enable banks to connect to their bill payment service. Because many of the electric company's customers are also customers of the bank, this will make it easier for those customers to pay their bills.

To facilitate this task, we will now create the Electric Company Service:

  1. Open Visual Studio and create a new project. Create a new Windows Console application in C:\WCFHandsOn\Chapter5\Before\PartIII.

    Name the project GenericElectricCompany and name the solution Transactions.

  2. Add a reference to System.Workflow.

  3. Add a reference to System.Transactions.

  4. Add a reference to System.Configuration.

  5. Add a new class named ElectricBillPay.cs.

  6. Modify the content of the App.Config file to resemble the following:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ServiceModel;
    using System.Transactions;
    namespace GenericElectricCompany
    {
        [ServiceContract]
        public interface IElectricBillPay
        {
            [OperationContract]
            [TransactionFlow(TransactionFlowOption.Required)] //Added for Transaction Support
            bool PayBill(int account, decimal amount);
    
            [OperationContract]
            decimal GetBalance(int account);
    
        }
    
        //Added for Sessions
        [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    
        class ElectricBillPay : IElectricBillPay
        {
            public bool PayBill(int account, decimal amount)
            {
                Console.WriteLine("Paying {0} towards bill on Account {1}", amount, account);
    
                return true;
            }
            public decimal GetBalance(int account)
            {
                return 0;
            }
        }
    }

    Note

    Note that in the code you've entered, you've placed an attribute on the PayBill operation that states that TRansactionFlowOption is required.


  7. Add a new application configuration file to the project named App.Config.

  8. Modify the content of the App.Config file to resemble the following:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration><appSettings>
      <!-- use appSetting to configure base address provided by host -->
      <add key="baseAddress" value="http://localhost:8080/GenericElectricBillPayService" />
    </appSettings>
    
    <system.serviceModel>
      <services>
        <service
            type="GenericElectricCompany.ElectricBillPay"
    >
        <!-- use base address provided by host -->
        <endpoint address=""
                  binding="wsHttpBinding"
                  bindingConfiguration="transactionBinding"
                  contract="GenericElectricCompany.IElectricBillPay" />
        </service>
      </services>
      <bindings>
        <wsHttpBinding>
          <binding name="transactionBinding" transactionFlow="true" />
        </wsHttpBinding>
      </bindings>
    
    
    </system.serviceModel>
    </configuration>

    Note that the configuration file includes a binding to flow transactions.

    Transactions will be used when paying a bill with this service. Because we're using the wsHttpBinding, we will be using WS-AT.

    Note

    If you've not already enabled WS-AT, you should do so using the instructions provided earlier in the chapter.


  9. Modify the content of the Program.cs file to resemble the following:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Configuration;
    using System.ServiceModel;
    namespace GenericElectricCompany
    {
        class Program
        {
    
            static void Main(string[] args)
            {
                // Get base address from app settings in configuration
                Uri baseAddressBillPay = new Uri(ConfigurationManager.AppSettings["baseAddress"]);
    
    
                // Instantiate new ServiceHost
                ServiceHost BillPayHost = new ServiceHost(typeof(ElectricBillPay),
     baseAddressBillPay);
                BillPayHost.Open();
                Console.WriteLine("Generic Electric Bill Payment Service is Online.");
                Console.WriteLine("-----------------------------------");
    
    
                Console.WriteLine("Press any key to terminate service.");
                Console.ReadKey();
    
                BillPayHost.Close();
            }
        }
    }

Creating the Banking Service

Next, you will consume the Electric Bill Payment Service at the bank, which will then expose that functionality to ATM customers.

  1. Create a proxy for the Bill Pay Service.

  2. Start the Generic Electric Bill Payment Service.

  3. Open a Visual Studio command prompt and navigate to the directory C:\WCFHandsOn\Chapter5\Before\PartIII\.

  4. Enter the following at the command prompt:

    "C:\Program Files\Microsoft SDKs\Windows\v1.0\Bin\SvcUtil.exe " http://localhost:8080
    /GenericElectricBillPayService/out:GenericElectricBillPay.cs

    SvcUtil.exe will generate two files, GenericElectricBillPay.cs and output.config.

    GenericElectricBillPay.cs will contain a generated proxy class for the Bill Pay service. Output.config contains the configuration information for the service that can be renamed to App.Config.

  5. Open Visual Studio, and create a new project in the current solution. Create a new Windows Console application in C:\WCFHandsOn\Chapter5\Before\PartIII.

    Name the project ConsumerBankingService.

  6. Add a reference to System.ServiceModel.

  7. Add a reference to System.Transactions.

  8. Add a reference to System.Runtime.Serialization.

    Open the GenericElectricBillPay.cs and note that the proxy specifies that transactionFloW is required:

    [System.ServiceModel.OperationContractAttribute (Action="http://tempuri.org
    /IElectricBillPay/PayBill", ReplyAction="http://tempuri.org/IElectricBillPay/PayBillResponse")]
    [System.ServiceModel.TransactionFlowAttribute(System.ServiceModel.TransactionFlowOption
    .Required)]
    bool PayBill(int account, decimal amount);
  9. Add a new class file named ConsumerBankingCore.cs. Modify the class to contain the following code:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ServiceModel;
    
    namespace ConsumerBankingService
    {
        [ServiceContract]
        public interface IConsumerBankingCore
        {
           [OperationContract]
           bool Deposit(int accountNumber, decimal amount);
           [OperationContract]
           bool Withdraw(int accountNumber, decimal amount);
           [OperationContract]
           decimal GetBalance(int accountNumber);
        }
    
        class ConsumerBankingCore : IConsumerBankingCore
        {
    
            public bool Deposit(int accountNumber, decimal amount)
            {
                Console.WriteLine("Depositing {0} into Account {1}", amount, accountNumber);
    
              return true; }
            public bool Withdraw(int accountNumber, decimal amount)
          {
              Console.WriteLine("Withdrawing {0} from Account {1}", amount, accountNumber);
              return true; }
            public decimal GetBalance(int accountNumber)
          { return 0; }
        }
    }
  10. Add a new class file named ConsumerBankingBillPayment.cs.

  11. Modify the class to contain the following code:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ServiceModel;
    
    namespace ConsumerBankingService
    {
        [ServiceContract]
        public interface IConsumerBankingBillPayment
        {
            [OperationContract]
            bool PayBill(int accountNumber, int accountNumberToPay, decimal amount);
    
         [OperationContract]
         string GetRegisteredBills(int accountNumber);
     }
    
    
     class ConsumerBankingBillPayment : IConsumerBankingBillPayment
     {
    
        public bool PayBill(int accountNumber, int accountNumberToPay, decimal amount)
        {
            bool success = false;
    
            ConsumerBankingCore banking = new ConsumerBankingCore();
            success = banking.Withdraw(accountNumber,amount);
    
            ElectricBillPayProxy proxy = new ElectricBillPayProxy("BillPay");
            success = success && proxy.PayBill(accountNumberToPay, amount);
            Console.WriteLine("Paying {0} from Account {1} towards GenericElectric account {2
    }", amount, accountNumber,accountNumberToPay);
    
            return success; }
    
         public string GetRegisteredBills(int accountNumber)
         { return "result"; }
    
       }
    }
  12. Modify the content of the Program.cs file to resemble the following:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ServiceModel;
    using System.Configuration;
    
    namespace ConsumerBankingService
    {
        class Program
        {
    
            static void Main(string[] args)
            {
    
            // Get base address from app settings in configuration
            Uri baseAddressCore = new Uri(ConfigurationManager.AppSettings ["baseAddressCore"]);
            Uri baseAddressBillPay = new Uri (ConfigurationManager
    .AppSettings["baseAddressBillPay"]);
    
            // Instantiate new ServiceHost
            ServiceHost CoreServiceHost = new ServiceHost(typeof(ConsumerBankingCore),
     baseAddressCore);
            CoreServiceHost.Open();
            Console.WriteLine("Consumer Banking Service is Online.");
            Console.WriteLine("-----------------------------------");
    
            ServiceHost BillPayServiceHost = new ServiceHost(typeof
    (ConsumerBankingBillPayment), baseAddressBillPay);
            BillPayServiceHost.Open();
            Console.WriteLine("Consumer Bill Pay Service is Online.");
            Console.WriteLine("-----------------------------------");
    
            Console.WriteLine("Press any key to terminate service.");
            Console.ReadKey();
    
            CoreServiceHost.Close();
            BillPayServiceHost.Close();
    
          }
       }
    }
  13. Open the App.Config file.

    The configuration file contains the configuration information to be a client for the Generic Electric Company Bill Payment Service.

  14. Modify the file to include references to the base addresses, and service endpoints.

    Your App.config file should resemble the following:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <appSettings>
        <!-- use appSetting to configure base address provided by host -->
        <add key="baseAddressCore" value="http://localhost:8080/ConsumerBankingService" />
        <add key="baseAddressBillPay" value="http://localhost:8080/ConsumerBankingService
    /BillPay" />
    </appSettings>
    <system.serviceModel>
    
         <client>
          <endpoint name="BillPay" address="http://localhost:8080/GenericElectricBillPayService"
            bindingConfiguration="WSHttpBinding_IElectricBillPay" binding="customBinding"
            contract="IElectricBillPay" />
       </client>
    
    
     <services>
         <service
             type="ConsumerBankingService.ConsumerBankingCore"
    >
         <!-- use base address provided by host -->
         <endpoint address=""
                   binding="wsHttpBinding"
                   contract="ConsumerBankingService.IConsumerBankingCore" />
      </service>
      <service
          type="ConsumerBankingService.ConsumerBankingBillPayment"
    >
        <!-- use base address provided by host -->
        <endpoint address=""
                    binding="wsHttpBinding"
                    contract= "ConsumerBankingService.IConsumerBankingBillPayment" />
         </service>
      </services>
    
      <bindings>
        <customBinding>
          <binding name="Secure conversation bootstrap binding
     51a11de1-ed0b-47b4-9893-c880ceaf4e22">
            <security defaultAlgorithmSuite="Default" authenticationMode="SspiNegotiated"
                defaultProtectionLevel="EncryptAndSign" requireDerivedKeys="true"
                securityHeaderLayout="Strict" includeTimestamp="true"
     keyEntropyMode="CombinedEntropy"
                messageProtectionOrder="SignBeforeEncrypt" protectTokens="false"
                requireSecurityContextCancellation="true" securityVersion="WSSecurityXXX2005"
                requireSignatureConfirmation="false">
              <localClientSettings cacheCookies="true" detectReplays="true"
                replayCacheSize="900000" maxClockSkew="00:05:00" 
     maxCookieCachingTime="10675199.02:48:05.4775807"
                replayWindow="00:05:00" sessionKeyRenewalInterval="10:00:00"
                sessionKeyRolloverInterval="00:05:00" reconnectTransportOnFailure="true"
                timestampValidityDuration="00:05:00" cookieRenewalThresholdPercentage="90" />
              <localServiceSettings detectReplays="true" issuedCookieLifetime="10:00:00"
                maxStatefulNegotiations="1024" replayCacheSize="900000" maxClockSkew="00:05:00"
                negotiationTimeout="00:02:00" replayWindow="00:05:00" inactivityTimeout="01:00:00"
                sessionKeyRenewalInterval="15:00:00" sessionKeyRolloverInterval="00:05:00"
                reconnectTransportOnFailure="true" maxConcurrentSessions="1000"
                timestampValidityDuration="00:05:00" />
        </security>
        <textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
            messageVersion="Default" writeEncoding="utf-8" />
        </binding>
        <binding name="WSHttpBinding_IElectricBillPay">
          <security defaultAlgorithmSuite="Default" authenticationMode="SecureConversation"
            bootstrapBindingConfiguration="Secure conversation bootstrap binding
     51a11de1-ed0b-47b4-9893-c880ceaf4e22"
            bootstrapBindingSectionName="customBinding" defaultProtectionLevel="EncryptAndSign"
            requireDerivedKeys="true" securityHeaderLayout="Strict" includeTimestamp="true"
            keyEntropyMode="CombinedEntropy" messageProtectionOrder="SignBeforeEncrypt"
            protectTokens="false" requireSecurityContextCancellation="true"
            securityVersion="WSSecurityXXX2005" requireSignatureConfirmation="false">
          <localClientSettings cacheCookies="true" detectReplays="true"
              replayCacheSize="900000" maxClockSkew="00:05:00" maxCookieCachingTime="10675199
    .02:48:05.4775807"
              replayWindow="00:05:00" sessionKeyRenewalInterval="10:00:00"
              sessionKeyRolloverInterval="00:05:00" reconnectTransportOnFailure="true"
              timestampValidityDuration="00:05:00" cookieRenewalThresholdPercentage="90" />
          <localServiceSettings detectReplays="true" issuedCookieLifetime="10:00:00"
                maxStatefulNegotiations="1024" replayCacheSize="900000" maxClockSkew="00:05:00"
                negotiationTimeout="00:02:00" replayWindow="00:05:00" inactivityTimeout="01:00:00"
                sessionKeyRenewalInterval="15:00:00" sessionKeyRolloverInterval="00:05:00"
                reconnectTransportOnFailure="true" maxConcurrentSessions="1000"
                timestampValidityDuration="00:05:00" />
            </security>
            <textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
                messageVersion="Default" writeEncoding="utf-8" />
            <httpTransport manualAddressing="false" maxBufferPoolSize="524288"
                maxMessageSize="65536" allowCookies="false" authenticationScheme="Anonymous"
                bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                mapAddressingHeadersToHttpHeaders="false" proxyAuthenticationScheme="Anonymous"
                realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"
                useDefaultWebProxy="true" />
          </binding>
        </customBinding>
      </bindings>
    </system.serviceModel>
    </configuration>

Creating the ATM Client

Create a proxy for the Bill Pay Service.

  1. Start the Generic Electric Bill Payment Service.

  2. Start the Consumer Banking Service.

  3. Open a Visual Studio command prompt and navigate to the directory C:\WCFHandsOn\Chapter5\Before\PartIII\.

  4. Enter the following at the command prompt:

    "C:\Program Files\Microsoft SDKs\Windows\v1.0\Bin\SvcUtil.exe " http://localhost:8080
    /ConsumerBankingService/BillPay /out:ConsumerBankingBillPay.cs

    SvcUtil.exe will generate two files, ConsumerBankingBillPay.cs and output.config. ConsumerBankingBillPay.cs will contain a generated proxy class for the Bill Pay service. Output.config contains the configuration information for your service; rename the file app.config.

  5. Open Visual Studio, and create a new project in the current solution. Create a new Windows Forms application in C:\WCFHandsOn\Chapter5\Before\PartIII.

  6. Name the project ATMClient.

  7. Add a reference to System.ServiceModel.

  8. Add a reference to System.Transactions.

  9. Add a reference to System.Runtime.Serialization.

  10. Add the file ConsumerBankingBillPay.cs to the project.

  11. Add the file App.Config to the project.

  12. Open Form 1, and add three text boxes, three labels, and a button.

  13. The text boxes should be named tbAccountNumber, tbElectricAccountNumber, and tbAmountToPay.

  14. One label should be to the left of each text box, identifying the type of information that should be entered: Account Number, Electric Account Number, and Amount to Pay.

  15. The button should be named btnPayBill and have the caption Pay Bill.

  16. Double-click on the button and modify the btnPayBill_Click method to resemble the following:

    private void btnPayBill_Click(object sender, EventArgs e)
    {
    ConsumerBankingBillPaymentProxy proxyBillPay =
      new ConsumerBankingBillPaymentProxy("BillPay");
      bool success = proxyBillPay.PayBill(
        Convert.ToInt32(tbAccountNumber.Text),
        Convert.ToInt32(tbElectricAccountNumber.Text),
        Convert.ToDecimal(tbAmountToPay.Text));
    
    if (success)
    {
      System.Windows.Forms.MessageBox.Show(
        "Bill payment successful!");
    }
    else
      {
        System.Windows.Forms.MessageBox.Show(
          "Bill payment failed!");
      }
    }

Testing the Solution

You are now ready to test your solution end-to-end.

  1. Start the Electric Company Service.

  2. Start the Banking Service.

  3. Start the ATM Client.

  4. Enter 12345 in the Bank Account Number field.

  5. Enter 67890 in the Electric Account Number field.

  6. Enter 50 in the Amount to Pay field.

  7. Press the button. Your screen should resemble what's shown in Figure.

    5. Screenshot of the resulting test.

Our consumer is at an ATM, requesting an action on the part of the bank, specifically to pay the consumer's electric bill.

The bank consumes a service from the electric company to perform the transaction.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows