Showing posts with label asynchronous. Show all posts
Showing posts with label asynchronous. Show all posts

Wednesday, October 30, 2013

Divorcing the UpdatePanel for Asynchronous Postbacks in Web Forms Isn't Easy

Point MVC vs. web forms here in the effort to make ajax calls to the server for asynchronous postbacks. This experience made me realize how nice MVC is from this respect I'm about to highlight.

For years I used ASP.NET UpdatePanels in web forms development and overall they have done the job. However, anyone that has used them on mildly complex pages knows, you can't just have 1 UpdatePanel. Nope, you need 2 with the 2nd one having a trigger pointing to the 1st so partial updates will work properly. The end result: a tangled web of UpdatePanel goo.

Recently I decided to use jQuery making AJAX calls to ASP.NET page methods instead as an alternative to UpdatePanels. The jQuery in it of itself may not be 100% cleaner but it works really well and is straight forward. So I create my ASP.NET page method, write some jQuery to make a call to my new page method asynchronously, and walla I have bypassed the UpdatePanel, yeah!

Not so fast...

Caveat #1: Page Method Must Be Static. This will by the way not be ideal for anyone doing Dependency Injection as you will have to Publicly resolve dependencies through methods like: Global.GetInstance()

Caveat #2: Can't see my server side controls 
from the static method called (because of #1 above and how it's called from the client).

Caveat #3: Normal postbacks later that access or post bound clientside elements complain of security and EnableEventValidation issue.

Caveat #4: Be prepared to query element values via Request.Form and not through control instances.

End result: A worse mess than using an UpdatePanel.

Recommendation: If you are doing ASP.NET web form development here are the (3) choices I recommend:

  1. Deal with full postbacks like it's 2002
  2. Use an UpdatePanel for asynchronous postbacks and don't mix client side data binding asynchronously with JavaScript.
  3. Ditch ASP.NET web forms and do MVC (hey no hater here, I stick up for web forms a lot - just know how the tool operates)
I want to show through an example all of what I just described. So 1st, lets look at a simple example to populate a ASP.NET ListBox server control's items using an AJAX call to the server to just see how this works. We'll make a DropDownList that contains the 'Periods' during a school day. Upon selection we want to have the classes populated in the ListBox. The goal is to do this without having a full postback or use an UpdatePanel.

First a simple set of server controls:

<form id="form1" runat="server">
    <div>
       <asp:DropDownList ID="DropDownList1" runat="server">
          <asp:ListItem Text="Please Select..." Value="0"></asp:ListItem>
          <asp:ListItem Text="1st Period" Value="1"></asp:ListItem>
          <asp:ListItem Text="2nd Period" Value="2"></asp:ListItem>
       </asp:DropDownList>
    </div>
    <div>
       <asp:ListBox ID="ListBox1" runat="server" Width="400px" SelectionMode="Multiple"></asp:ListBox>
    </div>
    <div>
       <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
    </div>
    <div>
       <asp:Button ID="Button1" runat="server" Text="Submit" OnClick="Button1_Click" />
    </div>  
</form>


Next we need a Static server-side event with the [WebMethod] attribute added that our jQuery we will make can make an AJAX call to and will return a collection of 'School Classes' for the 'Period' selected:
public partial class Default : System.Web.UI.Page
{
   protected void Page_Load(object sender, EventArgs e)
   {
   }

   [WebMethod]
   public static List<SchoolClasses> DropDownList1_SelectedIndexChanged(string id)
   {
 if (id == "0")
 {
    return null;
 }

 //Generic code representing call probably to retrieve values from the database
 var classesPeriod1 = new List<SchoolClasses>()
    {
  new SchoolClasses(){ID = 1, Name = "Math 101"},
  new SchoolClasses(){ID = 2, Name = "Science 101"},
  new SchoolClasses(){ID = 3, Name = "Social Studies 101"},
  new SchoolClasses(){ID = 4, Name = "Spanish 101"}
    };
 var classesPeriod2 = new List<SchoolClasses>()
    {
  new SchoolClasses(){ID = 5, Name = "Art 101"},
  new SchoolClasses(){ID = 6, Name = "Music 101"},
  new SchoolClasses(){ID = 7, Name = "English 101"},
  new SchoolClasses(){ID = 8, Name = "Global Studies 101"}
    };

 List<SchoolClasses> results = null;
 if (id == "1")
 {
    results = classesPeriod1.ToList();
 }
 if (id == "2")
 {
    results = classesPeriod2.ToList();
 }

 //this.Label1.Text == "You selected a id = " + id;

    return results;
 }

   protected void Button1_Click(object sender, EventArgs e)
   {
      var selectedItems = this.ListBox1.Items.Cast<ListItem>().Where(li => li.Selected).ToList();
      //The collection is empty?? There were selected items though...
   }
}

public class SchoolClasses
{
   public int ID { get; set; }
   public string Name { get; set; }
}

Lastly, we need the jQuery that is wired up to the 'change' event of the DropDownList that will call our new Static event server-side and populate the ListBox with the results.
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
   <script type="text/javascript">

      $(document).ready(function () {
         DropDownList1_OnChangeHandler();
      });

      function DropDownList1_OnChangeHandler() {
         // Add the page method call as an change handler for DropDownList1.
         // This will call the static page method, and use the returned results to populate ListBox1.
         $('#<%=DropDownList1.ClientID %>').change(function () {
            var val = $(this).val();
            var text = $(this).children("option:selected").text();
            var $timePeriod = $('#<%=ListBox1.ClientID %>');
            $timePeriod.attr('disabled', 'disabled');
            $timePeriod.empty();
            $timePeriod.append('<option value="0">< Loading Please Wait... ></option>');
            $.ajax({
               type: "POST",
               url: "Default.aspx/DropDownList1_SelectedIndexChanged",
               contentType: "application/json; charset=utf-8",
               dataType: "json",
               data: "{'id':'" + val + "'}",
               success: function (schoolClasses) {
                  $timePeriod.removeAttr('disabled');
                  $timePeriod.empty();
                  if (schoolClasses.d == null) {
                     return;
                  }
                  $.each(schoolClasses.d, function (i, schoolClass) {
                     $timePeriod.append('<option value="' + schoolClass.ID + '">' + schoolClass.Name + '</option>');
                  });
               },
               error: function () {
                  $timePeriod.empty();
                  $timePeriod.append('<option value="0">< Error Loading classes ></option>');
                  alert('Failed to retrieve classes.');
               }
            });
         });

      }
   </script>


If you built this example and ran it, the ListBox would populate as we would like. In fact it's really fast too. Originally I thought, "Home Run!" So what's the problem? Well if we have a simple example this might work, but as we need to add to the page, we begin to run into hurdles.



Let's try to add in (2) more features to highlight what I'm talking about:

  1. Update a label to state which 'Period' we selected when calling server-side Static method.
  2. Iterate through the 'Selected Items' collection on submittal of the form
OK, I know what you might be thinking thus far; EASY! I've done this a million times. Well, let's dig in and look at some of the challenges I listed at the beginning of this post.

First, add this line to our Static server-side method:
this.Label1.Text == "You selected a id = " + id;

Next, add this code to the server-side button click event to iterate through the selected items.
protected void Button1_Click(object sender, EventArgs e)
{
  var selectedItems = this.ListBox1.Items.Cast<listitem>().Where(li => li.Selected).ToList();
  //The collection is empty?? There were selected items though...
}


Now try to run this code. There is already an error with trying to access the Label's .Text property. Why? See #1 and #2 in the original list of issues. In a static method on the page, I can not see or have access to the Page's instance. Makes sense actually. I making a quick call to the server, but not in the context of how ASP.NET would expect, but rather from the client. Still completely legal to have the Static method, just will not behave as we might have hoped.

Comment out the the code to change the Label's .Text property. Now run the page, select some values in the dropdown and press the 'Submit' button. This time we have another issue; a hard error stating: "Invalid postback or callback argument." Why? See #2, #3, and #4. The server did not originally send the data to populate the ListBox, so it is unaware of it. This will cause a security error unless the page's EnableEventValidation="false" property is set. However I don't like to do this because it opens up a security risk and would not be something I recommend in production applications.

However if we do set to false EnableEventValidation, then we still can't see the values from our control, because the Server did not provide them and has no knowledge of them. How do we get them? Well you could use the Request.Form values and manually dig through the posted form to scrape out the elements. At this point, I'm thinking... Yuck.

So in order to make my AJAX call work to interact with the server using asynchronous postbacks and avoiding the UpdatePanel I have to deal with the following:
  • Reduce the page's security by setting EnableEventValidation="false"
  • Not be able to see any of the control values I manipulated through their server-side properties.
  • Have to dig through Request.Form to manually find updated client values
  • Not be able to use Dependency Injection as intended
  • Have a disorganized mismatch between the client and server state since the right-hand doesn't know what the left is doing.
At this point I think, is an UpdatePanel so bad compared to all of what I just discovered? Let's review the UpdatePanel code again:
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
 <asp:ListBox ID="ListBox1" runat="server" Width="300px" SelectionMode="Multiple" AutoPostBack="True" OnSelectedIndexChanged="ListBox1_SelectedIndexChanged" />
    </ContentTemplate>
</asp:UpdatePanel>

Nope, that actually was not so bad. So the end result for a web forms applications was I stuck with the Update Panels and everything worked well in regards to asynchronous postbacks. As I mentioned at the beginning, I think this is where MVC really shines, because there is no state being tracked and updated by the server and causing at least a portion of this mess because of the mismatch between the client updates happening independent of the server knowing about it.

Hopefully you try all of this out and see the caveats of trying to do AJAX asynchronous postbacks to the server via jQuery before going to far down the road on a new project hoping this will work smoothly. There are as I've shown several 'pitfalls' to be aware of so just make sure you factor them in if going down this route.

Monday, January 2, 2012

Leveraging Parallelism in the .NET Framework 4.0 for Asynchronous Programming

As I have mentioned in some of my previous previous posts it's amazing how easy it is to preform asynchronous processing in the later versions of the .NET Framework especially in .NET 4.0. The abstractions provided within the framework expose methods and libraries to be able to easily parallelize your queries or run tasks in parallel without all the hassle of managing your own threads. The idea is to harness the power and avalaibale threads on multi-core platforms today without having to actual manage the thread allocation and use yourself. This functionality is exposed in PLINQ (Parallel Language Integrated Query) and the TPL (Task Parallel Library) and are what I will be highlighting today.

1st let's take a look at parallelizing your existing LINQ query's with PLINQ. We are going to work with a simple 'Employee' class that has a few properties and dummy methods (implementation code for dummy methods is not the focus here) and another class named 'Payroll' with a method named 'CalculatePayRate' as shown below:
Public Class Employee
Public Function FindAll() As List(Of Employee)
'Notice the use of collection initializers that were added in VS.NET 2010 for VB.NET
Dim Employees = New List(Of Employee) From
{
New Employee() With {.FirstName = "John", .LastName = "Smith", .JobCode = 2, .ServiceYears = 1, .Age = 25, .IsActive = True},
New Employee() With {.FirstName = "Allen", .LastName = "Conway", .JobCode = 4, .ServiceYears = 8, .Age = 32, .IsActive = True},
New Employee() With {.FirstName = "Jane", .LastName = "Hulu", .JobCode = 6, .ServiceYears = 12, .Age = 40, .IsActive = True},
New Employee() With {.FirstName = "Clark", .LastName = "Griswold", .JobCode = 1, .ServiceYears = 4, .Age = 52, .IsActive = False},
New Employee() With {.FirstName = "Paul", .LastName = "Jacks", .JobCode = 15, .ServiceYears = 70, .Age = 93, .IsActive = True}
}
Return Employees.ToList()
End Function

Public Property Age As Integer
Public Property FirstName As String
Public Property IsActive As Boolean
Public Property LastName As String
Public Property JobCode As Integer
Public Property PayRate As Decimal
Public Property ServiceYears As Integer

Public Sub DoSomething1()
'Code
End Sub

Public Sub DoSomething2()
'Code
End Sub

Public Sub DoSomething3()
'Code
End Sub

Public Function GetEmployeeList1() As List(Of Employee)
'Code here
End Function

Public Function GetEmployeeList2() As List(Of Employee)
'Code here
End Function

Public Function GetEmployeeList3() As List(Of Employee)
'Code here
End Function
End Class

Public Class Payroll
Public Function CalculatePayRate(ByVal JobCode As Integer, ByVal YearsOfService As Integer, ByVal Age As Integer) As Decimal
If (JobCode >= 1 And JobCode <= 5) And (YearsOfService <= 5) Then Return 10.5 ElseIf (JobCode >= 1 And JobCode <= 5) And (YearsOfService > 5) Then
Return 12
ElseIf (JobCode > 5 And JobCode <= 10) And (YearsOfService <= 5) Then Return 22.5 ElseIf (JobCode > 5 And JobCode <= 10) And (YearsOfService > 5) Then
Return 25
ElseIf (JobCode > 10) And (Age > 90) Then
'Reward hard work!
Return 150
Else
Return 8
End If
End Function
End Class
I have set up a simple method below named 'EmployeeCalcPayRateUsingPLINQ' which will demonstrate getting a list of active Employees using LINQ and then calling the 'CalculatePayRate' both synchronously the traditional way using a For-Each loop (commented out in code) and then using PLINQ to make the same call in parallel.

All you need to do to parallelize your LINQ queries is to add the 'AsParallel' call at the end of your query. This will expose several of the PLINQ extension methods like the one we will be using today: 'ForAll'. The ForAll(Of TSource) parallel extension method will take a lambda expression that defines the code or delegate of the method to be called in parallel. We can use .ForAll() when we need to perform some action on every item in the source. In our case we need to manipulate the Employee's .PayRate value. This could be done synchronously, but is a prime candidate to be done in parallel because 1 employee’s payrate has no bearing on another’s so they can be calculated and processed in parallel. Let's take a look at the code:
Public Sub EmployeeCalcPayRateUsingPLINQ()
Dim Employees As List(Of Employee) = Nothing
Dim myPayroll As New Payroll
Dim myEmployee As New Employee

'Get a list of all employees
Employees = myEmployee.FindAll()

'Using LINQ to Objects, create a query that contains all 'Active' employees
Dim ActiveEmpQuery = From Emp In Employees
Where Emp.IsActive
Select Emp

'Using brute-force syncronous loop processing to calculate and set the payrate for each Employee object:
'Uncomment to try syncronous loop version
'For Each Emp As Employee In ActiveEmpQuery
' Emp.PayRate = myPayroll.CalculatePayRate(Emp.JobCode, Emp.ServiceYears, Emp.Age)
'Next

'Parallelize the LINQ query by calling the .ForAll extension method.
'Pass in a Lambda Expression that defines the delegate to be called in parallel
'that will calculate the payrate on each Employee instance.
ActiveEmpQuery.AsParallel.ForAll(Sub(Emp)
Emp.PayRate = myPayroll.CalculatePayRate(Emp.JobCode, Emp.ServiceYears, Emp.Age)
End Sub)
End Sub
As you can see with the example above, the PLINQ code and the traditional For-Each loop are the same line of code. However, the real gain here is in the time to process. If the list of active employees is 5000, the PLINQ .ForAll method will be able to use any available threads to run in parallel vs. the traditional loop which will process all 5000 records 1 at a time. The net gain is in the reduction of processing time since the PLINQ will run in parallel.

The Task Parallel Library (TPL) is another technology available in the .NET 4.0 Framework and is exposed in a set of APIs in the System.Threading and System.Threading.Tasks namespaces. The TPL will allow us to parallelize 'Tasks'. We can use the TaskFactory.StartNew(Action) method to start a task in parallel. This allows us to call several method in parallel that otherwise would have been to called synchronously. For example if we have (3) methods to call all which are unrelated and non-dependent of each other but must be called to complete processing, TPL can assist greatly. Especially if the call to the 1st method is longer running and blocks subsequent calls for being made. Using TaskFactory.StartNew(Action) is good for calling methods that return a result, and I will also highlight the Parallel.Invoke() method for calling methods with no return value in parallel. So let's 1st look at the code to create Tasks to be run in parallel and then extract their results:
Public Sub EmployeeProcessingUsingTPLTasks()
Dim Results1 As List(Of Employee) = Nothing
Dim Results2 As List(Of Employee) = Nothing
Dim Results3 As List(Of Employee) = Nothing
Dim AllEmployeeData As New List(Of List(Of Employee))
Dim Emp As New Employee()

'Create a Task that will call the Employee.GetEmployeeList1() method having its results dumped into 'Task1'
Dim Task1 = Task(Of List(Of Employee)).Factory.StartNew(Function() Emp.GetEmployeeList1())
'Create a Task that will call the Employee.GetEmployeeList2() method having its results dumped into 'Task2'
Dim Task2 = Task(Of List(Of Employee)).Factory.StartNew(Function() Emp.GetEmployeeList2())
'Create a Task that will call the Employee.GetEmployeeList3() method having its results dumped into 'Task3'
Dim Task3 = Task(Of List(Of Employee)).Factory.StartNew(Function() Emp.GetEmployeeList3())

'Note: each call to [Task].Result ensures that the asynchronous operation is complete before returning (built in functionality)
'Add the List from the Task1.Result to the collection in location '0'
AllEmployeeData.Insert(0, Task1.Result)
'Add the List from the Task2.Result to the collection in location '1'
AllEmployeeData.Insert(1, Task2.Result)
'Add the List from the Task3.Result to the collection in location '3'
AllEmployeeData.Insert(2, Task3.Result)
End Sub
In the example code below Tasks are started and run and their results are available from calling the Task.Result() property. Note that a call to [Task].Result ensures that the asynchronous operation is complete before returning (built in functionality). So this is where a synchronous behavior could appear if accessing a Result from a function that had not yet completed; the thread would block until its results were available. However the Tasks themselves were all run in Parallel and you could use this method to also call methods that have no return value, and which the .Result property didn't need to be accessed.

The last example I will show is using the Parallel.Invoke method to run 1..n methods in parallel. The code is pretty straight forward so let's look at calling a few methods on the Employee class in parallel:
Public Sub EmployeeProcessingUsingTPLParallelInvoke()
Dim Emp As New Employee()

'Make parallel individual calls via the TPL's Parallel.Invoke
Parallel.Invoke(Sub() Emp.DoSomething1(),
Sub() Emp.DoSomething2(),
Sub() Emp.DoSomething3())
End Sub
As you can see individual 'DoSomething()' methods were called in parallel and handled by the TPL. I think this can be helpful when needing to call several individual non-dependent methods but yet to another process behave as an aggregate or composite of calls needed to complete a single operation.

I have just discussed and provided a few examples of using PLINQ and the TPL in the .NET Framework 4.0. These examples barely scrape the surface of these topics and some of the lower level performance relationships that exist in relationship to the hardware the code it is trying to expose. I recommend trying out these examples and then building upon them. At any rate you can see how powerful just a few lines of code can be to any of your .NET applications.

Saturday, September 26, 2009

How To: Make an asynchronous call from an AJAX Modal Popup

I have been asked a few times and seen some confusion occasionally around making an asyncronous postback call from an AJAX Modal Popup. The purpose of doing this is to be able to make a server side call from the Modal Popup without having it dissapear or 'Hide', which then reverts the view back to the main page.

There is really no low level magic to making this happen, but rather it just involves making sure the proper AJAX UpdatePanels, and associated AsyncPostBackTriggers are placed in the correct locations.

I think the confusion sometime lies around where the UpdatePanel is to be located. I have seen times where the developer feels it should wrap the entire control referenced by the modalpopupextender, but actually it only needs to be placed around the individual control(s) causing the postback within the modal popup.

For this example, I made a simple page with a 'Show AJAX Modal Popup' button that displays the popup. Then a button within the modal popup calls its server side click event to change the label's 'Text' property. This is all done without the modal popup ever closing.

First, take a look at the source for the .aspx page:



<%@ Register Assembly="AjaxControlToolkit, Version=3.0.30512.20315, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e"
Namespace="AjaxControlToolkit" TagPrefix="AjaxControlToolkit" %>












PopupControlID="pnlTestPostback" BackgroundCssClass="modalBackground" DropShadow="true">







Second, look at the code behind:


Partial Public Class _Default
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
End Sub
Protected Sub btnShowModalPopup_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnShowModalPopup.Click
If Page.IsValid Then
Me.ModalPopupExtender1.Show()
End If
End Sub
Private Sub btnTestPostback_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnTestPostback.Click
Me.lblMessage.Text = "We made an asynchronous call server side from a modal popup."
End Sub
Private Sub btnCloseModalPopup_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnCloseModalPopup.Click
'Reset the text back to its default message:
Me.lblMessage.Text = "Press to test postback without full postback."
Me.ModalPopupExtender1.Hide()
End Sub
End Class


As you can see the trick was the placement of the UpdatePanel and having an AsyncPostBackTrigger in the UpdatePanel containing the label, to the '
btnTestPostback' click event. You may ask why both controls (label and button) didn't just go in the same UpdatePanel; well I was trying to show an example that could be more scalable to a larger ModalPopup with a greater number of control that could not be placed in the same UpdatePanel.


If you ever have any problems getting this implemented on a larger project, I recommend removing the UpdatePanels and associated triggers, and adding them back in carefully one at a time to get the behavior you are trying to accomplish.