Scott Hanselman

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

February 1, '12 Comments [72] Posted in ASP.NET
Sponsored By

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

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Wednesday, February 01, 2012 1:49:20 AM UTC
Sigh... sorry Scott, but that's bad code. We're replacing SQL strings with expression trees and delayed query execution, but there's no separation of concerns, just a big soup. Please don't show/teach stuff like this. It's the main reason we end up with a ton of newbies who actually use this in production and then the Ruby guys make fun of us. Then the good guys get tired of fighting this perception and leave as well...
Peter
Wednesday, February 01, 2012 1:58:26 AM UTC
"Wait, that doesn't suck at all." - well there is still the "if (!IsPostBack)" line!

Presumably paging parameters can be passed back from the gridview? That Select method looks like it's returning a full dataset on every call.
Wednesday, February 01, 2012 2:04:33 AM UTC
What is your concern? The grid view? The codebehind? It's a page controller vs. a front controller. You can separate concerns more with a repository, or use Dapper or Massive for the DAL, but how does that give you separation of concerns more than this? It doesn't. I'm just showing Web Forms syntax as a kind of view engine over a page controller. I don't see how this is killing kittens. I could certainly make a more separated version (more classes, a repo, IoC, etc.) if you like.
Scott Hanselman
Wednesday, February 01, 2012 2:05:41 AM UTC
The select method is returning a defered iQueryable which has a sort added from the grid. Peter doesn't like the deferred execution.
Scott Hanselman
Wednesday, February 01, 2012 2:14:34 AM UTC
This is no less separated than if you put data-access code in the body of your MVC action method. If you want to put data access logic into a separate class, you absolutely can. In this case, the data-access code is contained in a single code-behind method that is doing nothing but taking in parameters from model binding and returning a compos-able query. If you want to put that method on a separate class (like a repository or data gateway) you absolutely could, just like you can in MVC.
Wednesday, February 01, 2012 2:16:41 AM UTC
@David - it's returning an IQueryable, in this case from an EF Code First context object. That's not the entire result, it's an object that can be used to build a query. The GridView will automatically add the appropriate .OrderBy, .Skip and .Take method calls to ensure only the data required to render the current view is actually queried.
Wednesday, February 01, 2012 2:24:39 AM UTC
Now show some caching, you know... real world stuff.
Wednesday, February 01, 2012 2:32:04 AM UTC
Seriously, Peter.... it's the kind of thinking like that that propagates so much bad code out there in the first place. I'm so freaking tired of coders who think they are geniuses saying no code is decent without an IoC container. I've seen too many people like you take simple concept and complicate the hell out of it with DI and IoC, engineering for the sake of engineering. No one is going to replace the running database with MySql. No one is going to write a completely new implementation of the business logic service. So stop thinking you are better than everyone else. I'll take clean, simple, well thought-out code over hodgepodge overarchitected BS any day of the week an twice on Sunday.
Wednesday, February 01, 2012 2:40:16 AM UTC
The point is not that you can't have separate layers, of course you can. The point is that we should show good practices first. That would probably not be as sexy to demo, however; there would be more code to show. I would argue that if that's the case then MS should focus on fixing that problem instead. Demo me some new features that makes using good practices easier, not some cool trick that nobody should use, but somebody will, because it's on Scott's website and copied a million times in other blogs and that's all that ever comes up on a Google/Bing search.

On the second point: If you tie yourself down to IQueryable, no, you cannot just tear that piece out and use Dapper, Massive, or something else. You're tied to whatever LINQ provider you use. Although in theory you could replace providers, in practice doesn't always work this way. Not to mention that testing something like this would not be possible - not without a lot of work anyway.
Peter
Wednesday, February 01, 2012 2:47:45 AM UTC
Peter - I am always careful to disclaim my posts, as I realize folks copy paste. I don't want to NOT blog just because of a fear of copy pasting though.

I realize that tying down to IQueryable is somewhat limiting but NHibernate, l2s, and EFCF are decent choices. I'll see what a Massive or Dapper version would look like and report back. It would pass sorting in differently, of course.

Can you give me an idea of what you'd like to see around a best practice or big problem that I am not sharing on my blog? Or that I can work with the PMs to solve?
Scott Hanselman
Wednesday, February 01, 2012 2:55:29 AM UTC
@Robert: Where do you draw the line? Back in VB days globals were everywhere, with classic ASP we would mix code with markup, and so on. Would you do that kind of stuff today, even for a short demo? You don't separate concerns because you may change the database one day. Sure, that's a benefit, but the main reason is that you end up with silos of isolated code that is much easier to understand and maintain.
Peter
Wednesday, February 01, 2012 3:04:13 AM UTC
I'm with Peter on this one, if the grid itself is calling Take(), Skip() etc, and it's tied to IQueryable then 1) it is too limiting, 2) what would be the point then to move it to a separate repository etc?

Also, is it still imposible to unit test "Categories_Edit" (code "behind")? I haven't used WebForms since ASP.NET 2.0, but if it's still a PIA, then I can only continue to suggest *NEVER* to use WebForms for anything "serious"
Anonymous Coward
Wednesday, February 01, 2012 3:06:47 AM UTC
I think the thing Peter doesn't like (like me) is that the DAL shouldn't ever return a composable query (IQueryable). It should return data (ie IList). And then you will need paging parameters in that select method to pass to the DAL so the DAL can page directly on the database (or whatever source).
Marcel
Wednesday, February 01, 2012 3:09:29 AM UTC
Okay I'm late.
Marcel
Wednesday, February 01, 2012 3:20:31 AM UTC
Peter, I would love to follow your blog that regularly shows new features at a very high level, while maintaining proper coding practices. In the mean time I guess I am stuck with Hanselman's blog, where he takes his time to do this for the people that don't do it themselves.

I currently am in the python/django/sqlalchemy/nginx/etc space, but come from more of a .Net background so can relate to your comment of "Ruby guys making fun of us." I don't think it has anything to do with code like the quick examples Scott puts together, but rather the few that like to complain and not show the proper examples when they do it. I have actually been fairly successful in getting quite a few of my companies LAMP developers to embrace .Net platforms, but it took doing it by example.
Chris
Wednesday, February 01, 2012 3:33:57 AM UTC
@Damian & @Scott

With all due respect, saying "This is no less separated than if you put data-access code in the body of your MVC action method" is not really that true. I can put this kind of code in an MVC controller action method, call it in a unit test and ensure that my view will be handed the correct data before it renders (yes, I know that would be running the tests straight against the db).

Last time I Googled/Binged "ASP.NET Webforms Unit Testing" I didn't get many useful results. Whereas "MVC3 Controller Unit Testing" has a lot more there!

I actually like what you've demo'd here. But I think there's a lot of us out there that want to see where the testing hooks will be in Webforms.
Wednesday, February 01, 2012 3:45:55 AM UTC
@Scott: I enjoy and appreciate your blog; this one particular approach, however, I do not think should be used. As far as best practices to blog about, here's one of the top of my head: Where should validation go? Should it all be in the BOL/DAL? Should it be in the controller, on the page? How do we minimize the amount of code we write, but still have proper validation (both UI and DB validation)? With pros/cons all the while keeping in mind that the panacea would be to have it all in a single place.

@Marcel: Exactly

@Chris: I do not blog, not much of a writer, but I'll leave my two cents every now and then; that helps a bit, I like to think... I've "converted" a few myself, so I know where you're coming from.
Peter
Wednesday, February 01, 2012 3:51:08 AM UTC
Peter - Thanks for the ideas. Do you like the ASP.NET (both WebForms and MVC) validation technique using DataAnnotations and unobtrusive JS validation? Using EFCF it's all on the model and flows up to the JS, validates on client, server and in the database (if the database was generated or contains compatible tables).
Wednesday, February 01, 2012 4:02:11 AM UTC
There's nothing wrong with Scott's examples per se. Also, I believe those of you arguing for more abstraction are sort of missing the point....look at the markup and code for minute. Really let it soak in (feel the Gridviews/postbacks/Button1_Clicks, etc seep into your being). Still not abstract enough for you?

Now imagine the state of web technology over a decade ago when Web Forms led an army of VB6 developers to the world wide web. Now jump back to the present. See the issue? The problems that Web Forms was designed to solve (which it very well in its day) NO LONGER EXIST.

Web Forms had its day. Let's move on. Microsoft already has a very viable v3 MVC web framework that should really be their prescriptive web development platform.

James
James
Wednesday, February 01, 2012 4:37:59 AM UTC
@Marcel - The IQueryable will be paging on the database due to the deferred execution with LINQ to SQL.

@Scott - How strongly typed are these controls? Is the ControlBuilder actually building a FormView<Category>? If so, what properties are generic on these new controls? Would setting the SelectMethod in code-behind be type-safe? Does the CreateItem() method signature include dataItem as Category or still as Object?

Also, have you guys considered a server-side MVVM approach for WebForms where ViewState is completely turned off and only the ViewModel is persisted (using ControlState)? This would allow ViewModels to be reused between WebForms and WPF apps.
Wednesday, February 01, 2012 5:10:27 AM UTC
@Jeffrey - The control type itself is not generic. What happens is at page compilation time, we generate variables in the scope of the data-binding expressions that are typed to the type you specify in ItemType, which we then populate by casting the current data object to that type. The SelectMethod property is just a string which we use to look up the method on the page class (or another type/object you point us at) before we invoke it using reflection. I did experiment with some extension methods that would let you set the data method properties using lambda expressions but they proved slightly unwieldy due to the fact there is not strongly typed delegate to match against.

Are you referring to the private CreateItem method on things like Repeater and ListView? Because I can't find a public/protected method of that name that takes an object parameter called dataitem.

As for your last suggestion, that's basically what Web Forms MVP (http://webformsmvp.com) gives you today. I've built very large complex websites using Web Forms MVP where viewstate was disabled and all rendering was done from the view model in a single data-bind call just before the render stage. As the author of Web Forms MVP and the Program Manager for Web Forms at Microsoft, that's still my recommendation to people who want to get the highest separation of concerns in a Web Forms application with all the benefits of a pluggable IoC system and testability of presentation logic thanks to System.Web.Abstractions, while still keeping the benefits of the rich component and control system for behavior encapsulation and UI composition.
Wednesday, February 01, 2012 5:51:03 AM UTC
@Scott: I am using DataAnnotations and JS, but I'm finding them a bit limiting. I end up using the basic stuff and doing most of the validation on the server side or via custom JS where necessary.

For example I could use validation groups. A way to say that these fields "are together". When you have big forms showing the validation summary for all the fields in one place can be confusing.

Following this path, on the server side, having a way to unbind and validate in separate steps and by groups would also be nice. For example, sometime I hide part of the form via JS and on the server side I don't want to unbind and validate those fields. Unbinding would be OK, but definitely not the validation part. You could specify the properties to include/exclude with TryUpdateModel, but it's clunky imo.

Another nice-to-have would be to have message types, at least: error, warning, and information. Being able to programmatically add a "warning" for example to one field or one group of fields. For example: Your account expires in X days if you choose this option.

There are workarounds, you can make it work, but I haven't yet found a clean / elegant solution. I'm not looking for something that automates everything for me. I'm looking for the right balance between being sufficiently flexible and writing the minimum amount of code. In a way, I'm looking for the jquery-of-the-validation-problem.

Peter
Wednesday, February 01, 2012 9:35:46 AM UTC
I'd say WebForms sucks less, but still sucks.

RenderOuterTable="false"

Sigh... the ASP.NET WebForms developers just got smart too late, the framework will always be haunted by its past.
Mike
Wednesday, February 01, 2012 9:37:26 AM UTC
@Damian - WebFormsMVP does seems like an interesting project, but like most open source projects it suffers from sparse / old documentation and examples which is a huge barrier to entry.

Personally, I would like to see how these new features affect performance
Wednesday, February 01, 2012 9:39:45 AM UTC
@DamianEdwards

"As the author of Web Forms MVP and the Program Manager for Web Forms at Microsoft, that's still my recommendation to people who want to get the highest separation of concerns in a Web Forms application ...."

If you are talking about http://webformsmvp.com, which is part of the Outercurve foundation, then how about getting some documentation out on that? That website has been like that for more than ayear, and the wiki is like an abandoned ghost town. It may be very good, but it sure doesn't make a solid impression.
Mike
Wednesday, February 01, 2012 12:44:42 PM UTC
@Damian - Thanks for the info on WebFormsMVP. Admittedly, I have never investigated it much because the website made it look like a rather unsupported open source effort. I'll have to take a look at it.

You're right, I was confusing the way CreateItem/CreateRow works on the Repeater/GridView with how I would like it to work, or even better I suppose would be if RepeaterItem.DataItem and GridViewRow.DataItem were typed and always available (due to the bound ViewModel data being persisted and rebound on each postback).

What would be the ultimate would be if the designer generated partial inner classes on the page/user control for templates so that the controls in item templates (for rows on the Repeater or cells on the GridView) could have designer generated fields just like controls outside of templates, with this class having overridable page life cycle methods to manipulate those controls in a strongly-typed way.
Wednesday, February 01, 2012 12:47:43 PM UTC
WebForms has a lot of bigger problems than ViewState. The level and kind of abstraction and the whole event model can cause levels of entanglement (coupling), complexity, ugly, uncontrollable HTML and unintended behaviors that are just unacceptable, esp. in this age when HTML5/CSS/JS do so much but also demand more from us than over a decade ago when WebForms was born.

Sorry, but there's nothing they can do to WebForms to make me go back.
Wednesday, February 01, 2012 2:35:13 PM UTC
hi mr hanselman!
i'am a vb.net programmer
please wrote your code in vb.net
thank you
sorry for my bad english.
Wednesday, February 01, 2012 3:39:55 PM UTC
Scott, thanks for this example. I'm looking forward to the upcoming updates to webforms.

Here is my question/concern. Although I also use MVC and other frameworks, I have been building webforms sites for close to 10 years. I can't even estimate how many times I have used the ObjectDataSource. My concern lies with the iQueryable thing. Will there be a more direct migration path for users that simply want to rip out an ObjectDataSource and move directly to the model binding approach without having to rewrite the data layer to use iQueryable? I guess this kind of lines up with some of the micro ORM questions as well. Will we be able to call a method such as "GetCategories(sortBy)" the same way we did using an ObjectDataSource?
Thanks,
John
John
Wednesday, February 01, 2012 3:56:40 PM UTC
Really amazed at the amount of vitriol being displayed in the comments.

Look people it is up to you to write code that is clean and well seperated. Regardless of what framework you are using! WebForms has it's warts, but you can write maintainable code using it, and you can also write horrible code in MVC and Rails. If you really want to, implementing an MVP pattern that works in WebForms takes five minutes... literally!

In the end your choice of framework does not automatically make your code better or worse. It might make it easier/harder for you to write more maintainable code, but it doesn't preclude you from using your noggin. Writing clean maintainable code is an exercise in diligently thinking about your particular architecture and making informed decisions on how to keep concerns separated. No framework or technology can do this for you!

As far as Scott's example. It's meant to demonstrate the technology... not extraneous things like testing, encapsulating domain logic, validation, separation of concerns, etc...

At the end of the day MS job is to improve the experience with the technology. What developers do with it is up to them.

I feel a blog post coming on...
Wednesday, February 01, 2012 5:08:56 PM UTC
@Josh

You seem to be attributing to emotion what is NOT just an emotional response to WebForms. Not all ways of doing things are equal. I contend that WebForms is simply a flawed way of abstracting web development into a framework, when compared with MVC. I'm willing to talk about the specifics, but please do not mistake that we are poor workmen blaming our tools.

Of course it's possible to write bad code under any framework. I simply claim that under WebForms it's easier to do things that will be difficult to correct later, *because* the abstractions and components do not allow the proper degrees of freedom to correct issue or because interactions between them have more cases of hard to fix issues.

Richard
Wednesday, February 01, 2012 5:19:50 PM UTC
Good conversation, all. Understand that I'm not selling you anything (ASP.NET is free with Windows) and I really don't care if you use Web Forms or MVC or node.

I'm mostly an MVC or Web Pages guy thesesl days, myself. But the truth is that MVC usage is *maybe* 10% of ASP.NET usage right now. There are smart people doing interesting work on Web Forms today who are interesting in the fact that Web Forms CAN about clean HTML, do HTML5, use jQuery, do model binding, be testable with WebFormsMVP, and most importantly IS useful. You don't have to convert - I'm not asking you to. Just be aware it's out there and while you may not like its abstraction, others do.

I know you love MVC, but the fact is that ASP.NET (as I say "One ASP.NET") is what I'm interested in making better. You can be an enthusiast about one architecture or another, but it's all ASP.NET underneath. I move freely between them and choose to self-identify as an ASP.NET developer.
Scott Hanselman
Wednesday, February 01, 2012 5:36:22 PM UTC
I want to be clear I'm not calling anyone a bad name for either inventing or using WebForms. For me, it was a long experiment (about a decade now) that just added up to a particular conclusion.

In fact, perhaps it's my limited capacity that makes me yearn for a less abstract web framework, where some truly smart folks can make WebForms work for them because the added complexity (IMO) is not too much for them.

In the end, frameworks are for US, the developers, so we should choose what we like.
Wednesday, February 01, 2012 7:53:42 PM UTC
Cheers Hanselman,

I thought to myself "If I was the owner of a company, and a client wanted simple crud, would I consider this?". And yes, I probably would. It looks like it might be the most profitable route for my non-existent software company.

Good stuff

Wednesday, February 01, 2012 8:25:11 PM UTC
Ah, I'd missed that the GridView interacted directly with the IQueryable.

So does paging generate fresh urls or is it handled via postbacks? If I navigated to page 2 and then pressed F5, would I have to confirm that I wanted to re-post?
Wednesday, February 01, 2012 8:32:58 PM UTC
In the UpdateCategory page-behind method, you get the category from the DB by the CategoryID and then call TryUpdateModel. Is that because this is a quickie demo or does the ModelBinder work a little differently in this experiment than it does in MVC?

Why not have this UpdateCategory method?


public int UpdateCategory(Category category)
{
if(ModelState.IsValid) {
return _db.Save(category);
};
return 0;
}
Wednesday, February 01, 2012 10:41:22 PM UTC
As a fairly noob to all this stuff, how about some examples that go a level deeper? For example, how to utilize this with SQL stored procedures (our company app has been around too long to change that underlying aspect), or how to make an existing DAL IQueryable even?
Wednesday, February 01, 2012 11:23:21 PM UTC
Great post. I didn't realize people hated WebForms. (Maybe they don't and we just shouldn't feed the trolls not sure) I thought they just didn't use it, and I thought just because it was so much different from the web stuffs in the open source world. I like MVC a lot but I perfer WebForms for application heavy intranet applications, versus public facing heavy traffic web limited complexity apps\sites. IMHO the Unit Testing arguments against WebForms are weak given my experience with the real world value of unit testing web apps (i.e. its limited). Have people been able to unit test Javascript? I know I've had to write a lot more javascript with my MVC apps versus my WebForm apps. (AJAX in WebForms, seamless, in MVC, lots of javascript work) I'm thankful for jQuery, but I still hate it. I also find I end up with a lot more code and files with MVC actually. So I understand the love of MVC but not the hate of WebForms, I suspect its just typical zero-sum mindedness.
Thursday, February 02, 2012 12:40:53 AM UTC
@John - you don't have to return IQueryable from the select method. You can continue to use the same signature that ObjectDataSource supports, returning IEnumerable, and taking in the extra parameters for paging and sorting and doing it yourself. e.g.:


public IList<Category> GetCategories(string sortByExpression, int maximumRows, int startRowIndex, out int totalRowCount)
{
totalRowCount = _db.Categories.Count();

var categories = _db.Categories
.SortBy(sortByExpression)
.Skip(startRowIndex)
.Take(maximumRows)
.ToList();

return categories;
}


@David Wong - the GridView continues to use post backs to perform things like sorting and paging. In this sample, we've just put the GridView in an UpdatePanel by itself, so that doing sorting or paging won't force you to do a full post. That, coupled with the separate forms for edit, create, etc. result in nice PRG experience for this simple example.

@Scott Koon - yes that pattern is supported too. I tend to discourage it for updates against models with collection properties, which is typical if you're using EF code first model classes directly, as the model binder will be unable to populate the collection obviously and as such your object will have no children, causing the relationships to get deleted if you save it. But for the Create scenario here, we could've done that no problem.
Thursday, February 02, 2012 12:48:35 AM UTC
@Dave - the only restriction on the SelectMethod you use is that it must return an IEnumerable, or IEnumerable<T> if you've set the ItemType property. Inside the SelectMethod you can do whatever you like. If you want to call a stored procedure, you're free to do that using ADO.NET directly or an ORM like Entity Framework, Linq to SQL, NHibernate, Massive, Dapper, etc.

Making an existing DAL return IQueryables is a harder problem. You really need to use an ORM that has a LINQ query provider so that the IQueryable returned will actually transform the composed query into raw SQL that will executed on the database, not in your app tier. Things like Entity Framework, Linq to SQL and NHibernate have support for LINQ and thus IQueryable.

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.
Thursday, February 02, 2012 1:15:27 AM UTC
I started reading this cool article only to find out that it has become a realm of coding philosophy with no real meaning.
What's wrong with you guys. Has anybody taken the time to do what Scott said at the beginning and do this in ASP.NET MVC or Web Pages? That's the power of WebForms. The DataControls. Always has been. You don't like the IQueryable, write your own class and properties to separate anything you want. That's not the point in this article. I too prefer MVC, because i like clean html and http but i used to be a WebForms developer and i also like the way the whole .NET is evolving. Stop acting like you have to maintain facebook's database and keep up with the simple things that make a difference.
Panos
Thursday, February 02, 2012 2:15:08 AM UTC
Not to add hyperbole, but....

DEAR LORD....
1. This is SO testable.
2. This will SO prevent N+1 errors
3. This SO lends itself to inversion of control.

"This" is not the thing described in this post.
Thursday, February 02, 2012 3:29:21 AM UTC
I was leaving WebForms behind. That just changed my mind about aspx and crap code behind.

Really doesn't seem to be dead.
Jaime
Thursday, February 02, 2012 10:50:07 AM UTC
I think it's nice that you are still adding features like this to Web Forms, it still is the choice for me as a RAD tool. The issue is that MVC did everything right that WF did wrong, so it's difficult to be enthusiastic about such features that seemed to be shoe-horned into an already cluttered framework.

Obviously not all of us have the luxury of moving to MVC due to company policies etc. so we still have to use WF in production environments, but I know and love WF as it is now, I've learnt NOT to shove everything into the ASPX code and decided to write code-behind that is understandable to me and other users of it.

Push MVC if you think it's the next evolutionary step, but don't drag a 3-legged dog along with it, make your choice and stick with your roadmap as I think you are beating a dead-horse with WF personally. You guys did this with VB when .Net came along, it was always left behind C# but you still insisted on pushing it (I can see why, safe strategy and all), you have continued the VB/C# divide and you are now doing it with WF/MVC, it's nice to have a choice and all but sometimes I think you guys should just grab the bull by the horns, it sends mixed signals to the community IMO.
Thursday, February 02, 2012 1:17:54 PM UTC
@Damian Edwards - "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. "

I think this is key information and it is badly missing from the post which only says "You just need to provide a method here that returns an iQueryable, in this case, GetCategories.".

But the whole thing is very different if there are really two options: return IQuaryable or take in extra parameters and return IEnumerable.
You can use the first one to implement quick and simple apps and the second one to have more control and create something more complex (with proper separation of concerns and all).

Attila
attila
Thursday, February 02, 2012 2:13:06 PM UTC
@Scott Hanselman - "Can you give me an idea of what you'd like to see around a best practice or big problem that I am not sharing on my blog? Or that I can work with the PMs to solve?"

I would really love to read about displaying SSRS reports in an MVC application. I always end up adding a WebForms page with a ReportViewer control.

Oh, and if you happen to post about validation best practices (as Peter suggested), I think it would be great to include some complex examples, like:
Inter-property dependency: The valid value of a property depends on the value of another property on the same object.
Inter-object dependency: The valid value of a property depends on the value of another property on a different (but related, e.g. parent-child) object.
Attila
Thursday, February 02, 2012 5:43:56 PM UTC
@Scott, thank you for this post! It is really cool! This will be very helpful for my company, since we have quite a number of older WebForms sites which are so complex, that upgrading them all to MVC would be too time consuming at the moment, so upgrades for .NET 4.5 you've shown, could possibly allow more or less easier upgrades of those old sites to smth relatively advanced and easier to maintain, and closer to MVC than ever!
Many developers here don't realize that those upgrades are really really important for us, legacy site maintainers... They can make our life much easier!
Thursday, February 02, 2012 5:47:06 PM UTC
The problem with <asp:GridView in the past is that it generated really crappy front-end code. For instance, paging and sorting were performed with JavaScript based post backs, instead of updating the querystring. This meant Googlebot couldn't index past page 1, a major deal breaker for front-facing websites that rely on decent SEO. Also saving the state in the querystring had other benefits such as making the page's state bookmarkable, or for posting links on Twitter etc. This wasn't possible with GridViews.

Now I don't know if this has changed with more recent versions of ASP.NET, someone please enlighten me.
Dominic Pettifer
Thursday, February 02, 2012 8:25:46 PM UTC
Hi!

A little of topic, but I noticed you mentioned in the text of the post about the Web API: What is this project, and could you provide a link?

Also, in the image at the end of the post, there is 'Single Page Apps' rectangle: Does it refer to one actual page, or single-page Ajax applications? If Ajax, is this an MS project with a web site?

Thanks,
Schmulik Raskin
Thursday, February 02, 2012 10:30:40 PM UTC
Hello Scott
Put a download link ( or maybe my eyes are not good) and I do the same in MVC ;-)

Thank you,
Andrei
Friday, February 03, 2012 5:01:12 PM UTC
I'm with Schmulik: what's Web API? I now feel like I'm missing something obvious...
Saturday, February 04, 2012 1:29:12 AM UTC
WebForms, Repeaters, ADO.net, DataTables, DataViews, SPs are still sufficient. Are there any mind-blowing updates/samples/resources regarding OData and Reverse AJAX(Long-Polling)?

Thanks
Saturday, February 04, 2012 1:50:24 AM UTC
Have you see SignalR?
Scott Hanselman
Monday, February 06, 2012 9:37:09 AM UTC
Now that MIX 2012 has been cancelled by MS what will the future roadmap for ASP.NET? Is it still important for MS as it was once? Please understand that I am asking this because my career depends on it.
Amit
Monday, February 06, 2012 2:15:11 PM UTC
>>You have no reason not to
WCSF 2010 does not support routing..
Maxim
Monday, February 06, 2012 5:55:24 PM UTC
Scott & Damian,

Great post and appreciate all the work you guys are putting into asp.net. What I think is really missing in webforms is TempData (this is already in MVC). I don't think we can fully realize the PRG Pattern without it. For example you say

"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."

However, what happens when we want to add a simple success message when the update has happened ? Because there is no Tempdata (flash scope .. whatever) the RPG pattern in webforms starts to break down real quick. I think the changes you are doing are fantastic additions to webforms but please consider making Tempdata a first class citizen too! It would really help realize the RPG dream in webforms.
Serge
Monday, February 06, 2012 7:08:27 PM UTC
We all know the problems with webforms, even if your creating a little Intranet app your have to do a lot of work to make it accessible .

End of life webforms development(support yes enhance no).
Monday, February 06, 2012 9:15:53 PM UTC
Amit - It's is EXTREMELY important and isn't going anywhere.
Tuesday, February 07, 2012 7:40:02 AM UTC
Here are few tech blogs you really ought to read.

http://blog.brickred.com

We welcome your thoughts, opinions, Comments on our blog.
Wednesday, February 08, 2012 2:27:09 PM UTC
For those that were after a link to the Web API stuff, here's a link:

http://cc.bingj.com/cache.aspx?d=4568857797530172&w=915a7ef2,d6c256f4
RichB
Wednesday, February 22, 2012 9:11:09 PM UTC
Scott, I keep hearing this "Web Pages" term all of a sudden as a third option. I understand its connected with webmatrix, and maybe my lack of familiarity with webmatrix is the problem here. But what is the distinction between "Web Pages" and the other two?
Wednesday, February 22, 2012 9:49:45 PM UTC
Web Pages is Razor pages with inline scripting. It's like MVC but all in one page.
Thursday, March 22, 2012 10:59:18 PM UTC
I have to say that I absolutely love ASP.NET MVC. I was never really against ASP.NET Web Forms, but it was easy to get yourself in trouble or get some hard to debug problems (viewstate!). With ASP.NET MVC I control completely what I get and I get my things done faster.

I also really like the default sample that comes with it, I was able to get a personal page up and running in about 2 afternoons and that includes some special features I put in like displaying recent blog posts from another domain (with output caching!) or being able to generate my list of projects in different formats (e.g. text format for some carreer websites). Without any prior knowledge of ASP.NET MVC!

Big thanks to the ASP.NET MVC team. In many years this was really the first time I actually enjoyed developing a web site (feel free to forward them this compliment).

P.S.: Razor rocks!
Wednesday, April 25, 2012 8:38:27 PM UTC
It would be create if all controls could work that way, not only data controls.
Example:
<%@ Page Title="Home Page" Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication2._Default" %>
<asp:TextBox Text='<%: Page.MyCustomProperty %>' runat="server"></asp:TextBox>
namespace WebApplication2{
public partial class _Default : Page
{
public string MyCustomProperty {get; set;}
}
}
Saturday, May 26, 2012 10:57:37 AM UTC
MVC seemed great, but when I tried using it, I decided that I still prefer WebForms, ugly page lifecycle and all. Productivity is more important to me than technical sophistication. It is great that WebForms is still being improved.
Lee
Sunday, September 16, 2012 6:07:08 AM UTC
The bottom line is productivity and webforms delivers that.
John Lamb
Wednesday, January 16, 2013 4:04:14 PM UTC
Problem im having with this demo is the validation acts weird. If you attempt to add a record on a required field it just shows asterisks and totally ignores the required(error message) attribute on the model. Also is this a dynamic data project or a plain old web forms project? If the later then how can we use dynamic controls arent they supposed to be for dynamic data projects only?
steve hougom
Monday, February 11, 2013 10:06:34 PM UTC
The code sample is not working in my project. I am using Entity Framework with Database first model and I still have to write code in Page load event to get data bound to grid. Just passing IQueryable from select method is giving error too. I have to get the data first into a list so query gets executed and only then it is working.

What is going on here. I am using VS2012.
beena
Monday, February 18, 2013 12:46:19 PM UTC
Now that this has been released, I am trying to use it with a ListView using InsertItemTemplate and EditItemTemplate.

Problem is, I cannot find a way to use a DropDownList in the InsertItemTemplate; I can use it in the EditItemTemplate using SelectedValue="<%#: BindItem.ClientStatusID %>", but that syntax is not allowed in the InsertItemTemplate.

Net result is that when I TryUpdateModel in the EditMethod, the value of the DropDownList is honoured, but if I do the same in the InsertMethod the value is ignored (i.e. not set on the model).
Martin Robins
Monday, February 25, 2013 12:42:49 AM UTC
Thank you Microsoft for continuing to support Web Forms! These model binding features are awesome and will allow me to continue developing with a more modern style approach going forward.
Mike
Monday, February 25, 2013 12:48:20 AM UTC
I also agree with Serge. TempData would be huge in Web Forms and Web Pages
Mike
Monday, February 25, 2013 6:15:59 AM UTC
Can someone develop an "ULTIMATE" Hack so that we can use C# Razor Web Pages inside a MasterPage?
Mike
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.