Published articles on other web sites*

Published articles on other web sites*

One ASP.NET Sneak Peek: Elegant Web Forms and Snowballs in Hell

One ASP.NET Sneak Peek: Elegant Web Forms and Snowballs in Hell:
For the most part, I'm an ASP.NET developer. I don't need to specify MVC or Web Forms, because it's all One ASP.NET its core. My apps are often hybrids and include not just Web Forms or MVC but also SignalR and Web API.
Web Forms often gets picked on because of large View State, weird markup or maybe folks don't like the controls model. However, Web Forms has its place and it's getting even better with .NET 4.5. Here's a little sneak peek of some cool ideas Damian Edwards and the team have been working on for the next version of ASP.NET.
As a place to start, remember that ASP.NET routing started in MVC and moved into core ASP.NET. Routing is useful in all ASP.NET applications - MVC, Web Pages and Web Forms. Model Binding is coming to Web Forms as well, as well as Strongly Typed Data Controls and some other features that make both the code and the result pretty compelling. Dare I say, elegant. Elegant Web Forms? Madness! Who is this fool?
Here's a sortable grid with Create, Edit, Delete in Web Forms 4.5. An experiment for you, Dear Reader, would be to do the same thing I'm doing here in ASP.NET MVC or Web Pages.
Do note that this is fresh off Damian's laptop, and it's a experiment.
First, note the clean URLs. Use Routing, Web Forms people. You have no reason not to.
Clean URLs in WebForms. Scandalous.
Here's what it'll look like:

Databinding in ASP.NET Web Forms 4.5 Sneak Peek
Right now in this experiment, there is this routing table. Personally I'd like a convention for CRUD to make this one line.
Routes.MapPageRoute("CategoriesNew", "Categories/New", "~/Categories_New.aspx");
Routes.MapPageRoute("CategoriesEdit", "Categories/{id}", "~/Categories_Edit.aspx");
Routes.MapPageRoute("CategoriesDelete", "Categories/{id}/Delete", "~/Categories_Delete.aspx");
Routes.MapPageRoute("Categories", "Categories", "~/Categories.aspx");


Here's the Grid. Now, before you declare that's too freaky, take a look and note there's a lot of functionality going on here in not too many lines. ItemType (was ModelType in the Developer Preview) is
strongly typing this grid to the Category model. Notice SelectMethod. You just need to provide a method here that returns an iQueryable, in this case, GetCategories.



UPDATE with NOTE: See the comments below. Damian Edwards says: "That said, you don't need to return IQueryable. You can happily return an IEnumerable and just take in the extra parameters that the GridView will give you to ensure you can retrieve only the data for the currently requested page and sorted by the chosen column."



<asp:GridView runat="server" ID="categoriesGrid" CellSpacing="-1" GridLines="None"
  ItemType="VS11Experiment.Model.Category" DataKeyNames="CategoryId"
  AutoGenerateColumns="false"
  AllowPaging="true" AllowSorting="true" PageSize="5"
  SelectMethod="GetCategories">
  <Columns>
      <asp:DynamicField DataField="Name" />
      <asp:DynamicField DataField="Description" />
      <asp:TemplateField>
          <ItemTemplate>
              <a runat="server" href='<%# GetRouteUrl("CategoriesEdit", new { id = Item.CategoryId }) %>'>edit</a>
              <a runat="server" href='<%# GetRouteUrl("CategoriesDelete", new { id = Item.CategoryId }) %>'>delete</a>
          </ItemTemplate>
      </asp:TemplateField>
  </Columns>
  <EmptyDataTemplate>
      No categories found.
  </EmptyDataTemplate>
  <SortedAscendingHeaderStyle CssClass="asc" />
  <SortedDescendingHeaderStyle CssClass="desc" />
  <PagerStyle CssClass="pager" />
</asp:GridView>


"OK, Hanselman, what hellish code-behind are you going to show us now? What Satan's spawn have you been shining us on with all this time only to spring the nasty stuff at the last minute? I know you crafty Microsoft types, always creeping around with your Legos and your Windows Phones."


Fine, you caught me.


public partial class Categories : System.Web.UI.Page
{
  private readonly DemoWhateverDataContext _db = new DemoWhateverDataContext();

  public void Page_Load()
  {
      if (!IsPostBack)
      {
          // Set default sort expression
          categoriesGrid.Sort("Name", SortDirection.Ascending);
      }
  }

  public IQueryable<Category> GetCategories()
  {
      return _db.Categories;
  }
}


Aargh! My eyes! Wait, that doesn't suck at all. Even better if I could hypothetically put the default sort on the GridView and lose the whole Page_Load.


Whatever database or repository or Web (HTTP) Service you like, as long as your data access layer returns some IQueryables and you're in a good place. Sorting happens via LINQ so your data access layer can do the work, not ASP.NET.


So listing categories in a grid is decent, what's Edit look like?


Editing with ASP.NET Web Forms 4.5


If you add Model Binding to ASP.NET WebForms you spend less time digging around in the Request object. Notice that we're not doing that all here.


See how the RouteData attribute on GetCategory pulls the id out of the URL Categories/1?


public partial class Categories_Edit : System.Web.UI.Page
{
  private readonly DemoWhateverDataContext _db = new DemoWhateverDataContext();

  public Category GetCategory([RouteData]int? id)
  {
      return _db.Categories.Find(id);
  }

  public int UpdateCategory(int categoryId /* Comes from the data control itself via DataKeyNames property */)
  {
      var category = _db.Categories.Find(categoryId);
      TryUpdateModel(category);
      if (ModelState.IsValid)
      {
          return _db.SaveChanges();
      }
      return 0;
  }

  protected void categoriesForm_ItemUpdated(object sender, FormViewUpdatedEventArgs e)
  {
      Response.RedirectToRoute("Categories");
  }

  protected void categoriesForm_ItemCommand(object sender, FormViewCommandEventArgs e)
  {
      if (e.IsCancel())
      {
          Response.RedirectToRoute("Categories");
      }
  }
}


Often folks point to ASP.NET MVC's ability to use PRG (Post Redirect Get) as a strength. Often PostBacks in WebForms are looked down upon. In this model above, we're also totally able to use the PRG interaction model in Web Forms. See how the item is updated and we redirect to a route.


And the categoryId on UpdateCategory() comes from the Form View that is HTTP posting the data back. Here's a snippet:


<asp:FormView runat="server" ID="categoriesForm" RenderOuterTable="false"
          ItemType="VS11Experiment.Model.Category" DataKeyNames="CategoryId"
          DefaultMode="Edit"
          SelectMethod="GetCategory" UpdateMethod="UpdateCategory"
          OnItemUpdated="categoriesForm_ItemUpdated"
          OnItemCommand="categoriesForm_ItemCommand">


Also, you know how in ASP.NET MVC you've got unobtrusive JavaScript validation that is driven by the model class itself?


Edit Category in ASP.NET Web Forms


In ASP.NET MVC one often uses EditorFor, and in Web Forms we've got Dynamic Control. The idea being that Dates get Calendars and you can replace the UI completely using a field template. That feature actually started in Web Forms Dynamic Data and then moved to ASP.NET MVC. Features move both ways when it's all ASP.NET underneath. See what I did there?


<EditItemTemplate>
  <ol>
      <li><label>Name:</label>
          <asp:DynamicControl runat="server" ID="name" DataField="Name" Mode="Edit" />
      </li>
      <li><label>Description:</label>
          <asp:DynamicControl runat="server" ID="description" DataField="Description" Mode="Edit" />
      </li>
  </ol>
  ...


And when these forms are POSTed, you'll need validation. Rather than Validation Controls, in this case since we already know about the model we can use unobtrusive validation, similar to ASP.NET MVC. The idea is push the best ideas into the core of ASP.NET and make common stuff easy while letting people work the way they want to work.


public class Category
{
  [ScaffoldColumn(false), Display(Name="Id")]
  public long CategoryId { get; set; }

  [Required, StringLength(100)]
  public string Name { get; set; }

  [StringLength(10000), DataType(DataType.MultilineText)]
  public string Description { get; set; }
}


Sure, ASP.NET Web Forms may not be your cup of tea just like ASP.NET MVC might not be either. But remember that it's all One ASP.NET, and you've got a number of tools in your toolkit. Pick the ones that make you happy.


One ASP.NET Diagram


*And no, Hell isn't a dirty word in this context. ;)


Related Links





© 2012 Scott Hanselman. All rights reserved.

No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...