Home > DeveloperSection > Articles > Leveraging ASP.NET Infrastructure

Leveraging ASP.NET Infrastructure


ASP.NET MVC ASP.Net  Mvc4 
Ratings:
0 Comment(s)
 1305  View(s)
Rate this:

Leveraging ASP.NET Infrastructure

Hi everyone in this article I’m explaining about leveraging asp.net infrastructure.

Introduction:

This article explains how to develop a stateful WCF service leveraging the ASP.NET infrastructure, ASP.NET HTTP Pipeline, using BasicHttpBinding, using an example of a miniature shopping cart.

Using the Code:

We shall follow a contract-first approach quite similar to an SOA implementation for this example.

1.       Create a blank solution named StateManagementWCF.

2.       Add a class library project named OrderServiceContract.

3.       Rename class1.cs to IOrderService.cs and insert the following code there in.

 

  using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Runtime.Serialization;

    using System.ServiceModel;

    using System.ServiceModel.Activation;

    using System.ServiceModel.Description;

    using System.ServiceModel.Web;

    using System.Text;

    namespace OrderServiceContract

    {

        [ServiceContract(Namespace = "OrderServiceContract",

         SessionMode = SessionMode.Allowed)]

        public interface IOrderService

        {

            [OperationContract]

            void StartPurchase();

 

            [OperationContract]

            string PlaceOrder(Item item);

 

            [OperationContract]

            string MakePayment(decimal amount);

 

            [OperationContract]

            string ShipOrder(string address);

 

            [OperationContract]

            void EndPurchase();

 

            // TODO: Add your service operations here

        }

 

        // Use a data contract as illustrated in the sample

        // below to add composite types to service operations.

        [DataContract]

        public class Item

        {

            [DataMember]

            public string ItemName { get; set; }

 

            [DataMember]

            public decimal Price { get; set; }

        }

    }

Here you first declare and define a service contract with session mode as allowed and then a data contract for the composite object item. With the StartPurchase operations, the buyer shall start the purchasing session. With the PlaceOrder operation, the buyer shall place order for an item. This operation can be called multiple times in the session to purchase multiple items. In the service instance itself, the total outstanding amount for the items purchased would be calculated and remembered.

4.       Now add another project of type WCF Service Application and name it as UsageService. Add a reference to the System.ServiceModel assembly. Rename the service to OrderService.svc.

5.       Have the following code in the OrderService.svc.cs file:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.ServiceModel.Channels;

using System.ServiceModel.Description;

using System.ServiceModel.Activation;

using System.ServiceModel.Web;

using System.Text;

 

namespace UsageService

{

    [AspNetCompatibilityRequirements(RequirementsMode =

         AspNetCompatibilityRequirementsMode.Allowed)]

    public class OrderService : OrderServiceContract.IOrderService, IDisposable

    {

        private decimal TotalAmount { get; set; }

        private decimal PaymentReceived { get; set; }

        private bool TransactionStarted {get; set;}

 

        public OrderService()

        {

            TotalAmount = 0;

            PaymentReceived = 0;

            TransactionStarted = false;

        }

 

        public void StartPurchase()

        {

            HttpContext.Current.Session["TransactionStarted"] = true;

            HttpContext.Current.Session["TotalAmount"] = 0;

            HttpContext.Current.Session["PaymentReceived"] = 0;

        }

 

        public string PlaceOrder(OrderServiceContract.Item item)

        {

            if (Convert.ToBoolean(HttpContext.Current.Session["TransactionStarted"]))

            {

                HttpContext.Current.Session["TotalAmount"] =

                  Convert.ToDecimal(

                  HttpContext.Current.Session["TotalAmount"]) + item.Price;

                return "Order placed for item " + item.ItemName +

                  " and total outstanding amount is $" +

                  HttpContext.Current.Session["TotalAmount"].ToString();

            }

            return "Shopping session not yet started";

        }

 

        public string MakePayment(decimal amount)

        {

            if (Convert.ToBoolean(HttpContext.Current.Session["TransactionStarted"]))

            {

                HttpContext.Current.Session["PaymentReceived"] =

                  Convert.ToDecimal(

                  HttpContext.Current.Session["PaymentReceived"]) + amount;

                return "Payment made of amount USD " +

                   HttpContext.Current.Session["PaymentReceived"].ToString() +

                   " and amount remaining to be paid is $" +

                   ((Convert.ToDecimal(HttpContext.Current.Session["TotalAmount"])) -

                   (Convert.ToDecimal(

                   HttpContext.Current.Session["PaymentReceived"]))).ToString();

            }

            return "Shopping session not yet started";

        }

 

        public string ShipOrder(string address)

        {

            if (Convert.ToBoolean(HttpContext.Current.Session["TransactionStarted"]))

            {

              if ((Convert.ToDecimal(HttpContext.Current.Session["TotalAmount"])) <=

                  (Convert.ToDecimal(HttpContext.Current.Session["PaymentReceived"])))

              {

                  return "Ordered items would be reaching" +

                         " at your doorstep soon. Thanks";

              }

              return "Please pay the full amount in advance in order to enable " +

                  "us ship your items, the outstanding amount is $" +

                  ((Convert.ToDecimal(HttpContext.Current.Session["TotalAmount"])) -

                  (Convert.ToDecimal(

                  HttpContext.Current.Session["PaymentReceived"]))).ToString();

            }

            return "Shopping session not yet started";

        }

 

        public void EndPurchase()

        {

            if (Convert.ToBoolean(HttpContext.Current.Session["TransactionStarted"]))

            {

                HttpContext.Current.Session["TransactionStarted"] = false;

            }

        }

 

        public void Dispose()

        {

        }

    }

}

Here, the OrderService class implements the IOrderService and IDisposable interfaces. The class is decorated with the attribute:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

This makes the WCF service leverage the existing ASP.NET HTTP pipeline and thereby get a sort of "license" to use the HttpContext.Current.Session object to store (remember) stateful data. During the StartPurchase operation, we initialize the data in session variables, thereby setting up the session. In every other operation, we check whether the session is started or not by checking theTransactionStarted Boolean variable which is stored in a session variable (and therefore remembered across calls).

6.       Now we shall modify the web.config. The modified web.config is presented below:

 

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

  <system.web>

    <compilation debug="true"/>

    <sessionState cookieless="false" mode="InProc"/>

  </system.web>

  <system.serviceModel>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

    <services>

      <service name="UsageService.OrderService"

               behaviorConfiguration="UsageService.OrderServiceBehavior">

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost/UsageService" />

          </baseAddresses>

        </host>

        <endpoint address="" binding="basicHttpBinding"

                  bindingConfiguration="OrderBinding"

                  contract="OrderServiceContract.IOrderService" />

        <endpoint address="mex" binding="mexHttpBinding"

                  contract="IMetadataExchange" />

      </service>

    </services>

    <behaviors>

      <serviceBehaviors>

        <behavior name="UsageService.OrderServiceBehavior">

          <serviceMetadata httpGetEnabled="true" />

          <serviceDebug includeExceptionDetailInFaults="true"/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

    <bindings>

      <basicHttpBinding>

        <binding name="OrderBinding" allowCookies="true">

          <security mode="None" />

        </binding>

      </basicHttpBinding>

    </bindings>

  </system.serviceModel>

  <system.webServer>

    <modules runAllManagedModulesForAllRequests="true" />

    <directoryBrowse enabled="true" />

  </system.webServer>

</configuration>

Here, the first thing we do is set the sessionState element's cookieless attribute to false, thereby supporting session cookies and specifying how the state would be maintained. We choose InProc (in-memory session data store) for now just for simplicity sake, as otherwise we would get diverted from our actual discussion to state management techniques in ASP.NET.

<system.web>

  <compilation debug="true"/>

  <sessionState cookieless="false" mode="InProc"/>

</system.web>

Next we set the ASP.NET compatibility as follows:

<system.serviceModel>

  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

Here, we enable the aspNetCompatibilityEnabled attribute of serviceHostingEnvironment. So IIS hosts the service, and IIS and the ASP.NET integrated modules and handlers provide the hosting environment. Next, we can observe that we have used basicHttpBinding here, which actually mimics the erstwhile ASP.NET Web Services. Now, in the binding configuration, we allow cookies:

<bindings>

  <basicHttpBinding>

    <binding name="OrderBinding" allowCookies="true">

      <security mode="None" />

    </binding>

  </basicHttpBinding>

</bindings>

The reason for doing this is that when the client first makes a call to the service through a proxy, a service instance gets created, a session is started, and a session cookie is returned to the client, which the client has to submit to the server with each successive service call in the session. In the session cookie, the session ID is stored, which when the service receives with each successive service call, it understands which client is calling it and what the session data are for that particular client session, given that there might be many such clients calling those service operations simultaneously. The session ID is unique for each session and distinct for all clients.

7.       Next, we create a virtual directory called UsageService in IIS and test the service by browsing OrderService.svc.

8.       Next, we add a Windows Forms Application project to the solution named as ShoppingClient, and add a reference to System.ServiceModel to it.

9.       Now we add a service reference to the ShoppingClient for the endpoint: http://localhost/UsageService/OrderService.svc.

10.   We then design the UI for the form, and finally add the following code in ShoppingForm.cs:

 

public partial class ShoppingForm : Form

    {

        private List<OrderServiceReference.Item> itemList = null;

        private OrderServiceReference.OrderServiceClient clientService = null;

 

        public ShoppingForm()

        {

            InitializeComponent();

            itemList = new List<OrderServiceReference.Item>();

        }

 

        private void AddItemsToList()

        {

            OrderServiceReference.Item itm = new OrderServiceReference.Item() { ItemName = "Bag", Price = (decimal)10.70 };

            itemList.Add(itm);

            itm = new OrderServiceReference.Item() { ItemName = "Boot", Price = (decimal)11.30 };

            itemList.Add(itm);

            itm = new OrderServiceReference.Item() { ItemName = "Basket", Price = (decimal)10.00 };

            itemList.Add(itm);

            itm = new OrderServiceReference.Item() { ItemName = "Box", Price = (decimal)20.07 };

            itemList.Add(itm);

            itm = new OrderServiceReference.Item() { ItemName = "Bat", Price = (decimal)1.93 };

            itemList.Add(itm);

        }

 

        private void ShoppingForm_Load(object sender, EventArgs e)

        {

            try

            {

                clientService = new OrderServiceReference.OrderServiceClient();

                clientService.StartPurchase();

                this.AddItemsToList();

            }

            catch (Exception ex)

            {

                txtMessage.Clear();

                txtMessage.Text = ex.Message + "\n" + ex.Source + "\n" +

                                  ex.StackTrace + "\n" + ex.TargetSite;

            }

        }

 

        private void btnExit_Click(object sender, EventArgs e)

        {

            Application.Exit();

        }

 

        private void btnMakePayment_Click(object sender, EventArgs e)

        {

            txtMessage.Clear();

            if (txtAmount.Text == String.Empty)

            {

                MessageBox.Show("Please enter amount first");

                return;

            }

            txtMessage.Text = clientService.MakePayment(

                                Convert.ToDecimal(txtAmount.Text.Trim()));

            txtAmount.Clear();

        }

 

        private void btnPurchaseBag_Click(object sender, EventArgs e)

        {

            txtMessage.Clear();

            txtMessage.Text = clientService.PlaceOrder(itemList[0]);

        }

 

        private void btnPurchaseBoot_Click(object sender, EventArgs e)

        {

            txtMessage.Clear();

            txtMessage.Text = clientService.PlaceOrder(itemList[1]);

        }

 

        private void btnPurchaseBasket_Click(object sender, EventArgs e)

        {

            txtMessage.Clear();

            txtMessage.Text = clientService.PlaceOrder(itemList[2]);

        }

 

        private void btnPurchaseBox_Click(object sender, EventArgs e)

        {

            txtMessage.Clear();

            txtMessage.Text = clientService.PlaceOrder(itemList[3]);

        }

 

        private void btnPurchaseBat_Click(object sender, EventArgs e)

        {

            txtMessage.Clear();

            txtMessage.Text = clientService.PlaceOrder(itemList[4]);

        }

 

        private void btnShipOrder_Click(object sender, EventArgs e)

        {

            txtMessage.Clear();

            txtMessage.Text = clientService.ShipOrder("DF-I, B-2/4, " +

               "PURBA ABASAN, 1582/1 RAJDANGA MAIN ROAD, KOLKATA - 700107, INDIA");

        }

    }

Here we maintain a list for items ordered and fill the same in method called AddItemsToList(),ow, during Form_Load, we call the StartPurchasing() operation to start the session with the service. We call the PlaeOrder(item) method to place orders, and the MakePayment(amount) method to make payments, and finally the ShipOrder(address) method to request the service to ship the ordered items. We see that we are doing nothing about state management at the client code, because the trick is done in the app.config.

 

11.   The App.config file (after modification) is presented as follows:

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <binding name="BasicHttpBinding_IOrderService"

               closeTimeout="00:01:00"

              openTimeout="00:01:00" receiveTimeout="00:10:00"

           sendTimeout="00:01:00"

            allowCookies="true" bypassProxyOnLocal="false"

          hostNameComparisonMode="StrongWildcard"

            maxBufferSize="65536" maxBufferPoolSize="524288"

               maxReceivedMessageSize="65536"

            messageEncoding="Text" textEncoding="utf-8"

               transferMode="Buffered"

            useDefaultWebProxy="true">

          <readerQuotas maxDepth="32" maxStringContentLength="8192"

               maxArrayLength="16384"

              maxBytesPerRead="4096" maxNameTableCharCount="16384" />

          <security mode="None">

            <transport clientCredentialType="None"

             proxyCredentialType="None"      realm="" />

            <message clientCredentialType="UserName"

             algorithmSuite="Default" />

          </security>

        </binding>

      </basicHttpBinding>

    </bindings>

    <client>

      <endpoint address="http://kummu-pc/UsageService/OrderService.svc"

          binding="basicHttpBinding"

         bindingConfiguration="BasicHttpBinding_IOrderService"

          contract="OrderServiceReference.IOrderService"

       name="BasicHttpBinding_IOrderService" />

    </client>

  </system.serviceModel>

</configuration>

 

Here we have set allow Cookies to true for the binding. The proxy automatically handles the session cookie returned by the service and submits the session cookie to the service each time a service call is made thereafter. All this plumbing is done transparent to the developer.


Don't want to miss updates? Please click the below button!

Follow MindStick