Showing posts with label WebForms. Show all posts
Showing posts with label WebForms. 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.

Friday, September 6, 2013

Why and How ASP.NET Web Forms Were so Crucial in the Success of .NET

I have been using ASP.NET just about since it's inception to the public around 2002ish (see: TechEd 2001 throws .NET to developers or .NET Release Date Announced (way to go VSLive! SanFran 02') for a little nostalgia). Nowadays it seems the dirty word of web technology is to even mention 'web forms' to be used typically in favor of MVC. This however is a misnomer because web forms are still a good tool in the proverbial toolbox of engineering technologies. I have done both and they both have their place. Personally as an architect I prefer MVC because of it's architecture compared to the event driven web forms, but honestly I like the RAD aspect of server controls. Yes I said it, the UI part in web forms is a bit easier than working with HTML helpers in MVC. Not that I would pick a web forms project simply because of that, but I still like it. It's easy, and easy is good.

I actually got in a mild argument on a Stack Overflow chat about 6 months ago as I was being discounted as a 'seasoned professional' because I had only done MVC for the last few years. It didn't take long for other 'seasoned professionals' to back me up and explain to this newb to pipe down a bit. Web forms have a deep history in the 10+ year history of .NET.

I'm not writing here about the differences (I've done that previously: ASP.NET Web Forms vs. ASP.NET MVC) but rather shed light to those newer to the industry of why web forms played such a crucial piece in the success of .NET. If you are a new developer to the industry (< 5 years experience) you may only know MVC and be one in the camp bashing web forms. Please take note that ASP.NET's version of MVC may have never existed if web forms didn't exist with their programming style to bridge the gap to .NET years ago.

Let me elaborate. Circa late 90's early 2000's a huge majority of developers on the Microsoft Stack were using a product (we all love, right?) called Visual Studio 6.0, a.k.a. VB6. And all of us were creating Windows Forms applications (and maybe a few COM components or ActiveX controls too). Those WinForm apps were all using an event driven (button click behind a form) style of programming. There were also those doing C++ at the time as well and they were eyeing another gap to bridge which VB6 did not have and that was true Object Oriented programming.

Along comes this new IDE and technology called VS.NET with it's .NET framework. I remember when 'Google Groups' was still big on the internet for forums usage and a lot in the industry made remarks like, "This .NET thing will not catch on" or "I've still to see why I need to move to .NET because I can do everything I need with VB6 or ASP". Luckily I was never of that attitude and was with companies willing to move quickly to .NET and away from VB6.

There really were (2) main technologies to develop in when VS.NET came about: WinForms and ASP.NET Web Forms (maybe 3 if you count .asmx web services which was the most touted but not required for use in building applications necessarily). The absolutely ingenious thing that Scott Gu and Microsoft did was make the ASP.NET programming model identical in most aspects to that of WinForm and yes even VB6. Let me demonstrate: "OK, take this new 'web page' called Default.aspx and drag a Button control onto the form. Now double click that button and their is your event handler. Now run the application. Guess what? You just built a web site." :D  The response: "What?!?!?! That was just like my VB6 apps, maybe this whole web apps technology and .NET is worth a try." Also thank goodness for C# as well to bridge this gap in the same manner for C++ developers looking for a little familiarity of their own and some true OO programming. I think in the same respect most C++ developers would have become Java developers if C# was not born. That's completely analogous to the importance of web forms played for bringing VB6 devs to .NET.

I remember back in the early 2000's there was stiff competition from Java, Perl, PHP, and the like as prominent technologies to win over devs off the Microsoft languages and IDEs from the soon to be retired Visual Studio 6.0. Now MVC (Model-View-Controller) is an old architecture concept that dates back to the 70's and 80's from Smalltalk but arguably is a tad more complex to learn than the RAD drag-and-drop event based programming model. Imagine if there were no web forms when VS.NET appeared in late 2001 and they introduced ASP.NET MVC only. Sure there would be some hardcore devs that would have like it, but the majority of the millions of VB6 developers would have taken 1 look at that and said, "No thank you". Web forms and their almost identical to VB6 style of programming (other than being stateless as probably the biggest difference) was absolutely key in my opinion to the success and adoption of VS.NET and .NET in general.

Kudos to those that probably thought along these lines when coming up with the .NET framework. Now 12+ years later and there is a SLEW of technologies at our fingertips on the Microsoft stack like our friend ASP.NET MVC. However we might not be anywhere close to where we are today without ASP.NET web forms and it's close to Windows Forms - VB6 programming similarities to bring over and attract the masses. So this may not make you choose web forms today as technology #1 to being your new project, but at least understand it's importance in the world of .NET.