{"id":5701,"date":"2015-06-11T07:39:00","date_gmt":"2015-06-11T07:39:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/webdev\/2015\/06\/11\/cool-asp-net-web-forms-features-in-2015-async-model-binding\/"},"modified":"2023-09-18T09:12:33","modified_gmt":"2023-09-18T16:12:33","slug":"cool-asp-net-web-forms-features-in-2015-async-model-binding","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/cool-asp-net-web-forms-features-in-2015-async-model-binding\/","title":{"rendered":"Cool ASP.NET Web Forms Features in 2015 \u2013 Async Model Binding"},"content":{"rendered":"<p>This is the first in a twice weekly series of blog posts that we\u2019re going to share from the Microsoft Web Development blog to showcase some of the really cool and mature features available in ASP.NET Web Forms for developers to use in their projects. \u00a0Web Forms is the development model that was deployed first with ASP.NET in 2001, and has been improved in every ASP.NET release since.\u00a0 Throughout this series of posts, we will build a sample application to track our travel information.\u00a0 Each update will provide a link to the source code so that you can follow along.<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/06\/2018.1-DefaultGrid.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/06\/2018.1-DefaultGrid.png\" alt=\"\" border=\"0\" \/><\/a><\/p>\n<p>In this first post, we\u2019re going to look at the model binding feature in web forms and see how it has been improved to provide for asynchronous operations in the new ASP.NET 4.6 framework in Visual Studio 2015.<\/p>\n<h2><\/h2>\n<h2>In the beginning<\/h2>\n<p>Many long-time ASP.NET developers are familiar with the various ways that you can deliver data to user interface components in web forms.\u00a0 We call that databinding to a control, and there are two primary techniques you can use:<\/p>\n<ul>\n<li>Declarative data-binding with data controls<\/li>\n<li>Code-behind, or \u201cmanual\u201d data-binding on the server<\/li>\n<\/ul>\n<p>Declarative data-binding is where all of our database code is placed into a SqlDataSource object on the ASPX page and a grid or some other user interface control references the id of the SqlDataSource to know how to work with a data resource.\u00a0 Let\u2019s start our first code sample with a simple grid bound to a table of trip information:<\/p>\n<p>The advantage of this technique is that all of the code for the user interface, server-side operations, and database commands are all in one file.\u00a0 The disadvantage with this approach is that when any of your database resources changes or matures, you need to touch all of your ASPX files to update and maintain those objects.\u00a0 This is particularly a problem as a database evolves over time.<\/p>\n<p>A manual code-behind method would connect data access to controls through a series of events that are appropriate to each control.\u00a0 The advantage of this approach is that custom code can be written to centralize data access using a repository class strategy.\u00a0 The disadvantage of this technique is that a lot more code needs to be written to connect these classes and I need to ensure that the data content of the grid is loaded in the correct event during the page lifecycle.\u00a0 I could put the contents of my grid into ViewState, but that would bloat the size of the page, a major faux pas in today\u2019s web.<\/p>\n<h2><\/h2>\n<h2>Along came Model Binding<\/h2>\n<p>With the release of ASP.NET 4.5 in 2012, a simpler and friendlier data binding solution was introduced called Model Binding.\u00a0 This was an adjustment of a similar feature that was developed for ASP.NET MVC so that the Web Forms framework could experience the productivity gains of this simplified programming model.\u00a0 The idea with Model Binding is that each data-bound control has four simple methods for each of the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Create,_read,_update_and_delete\">CRUD<\/a> operations:<\/p>\n<ul>\n<li>InsertMethod<\/li>\n<li>SelectMethod<\/li>\n<li>UpdateMethod<\/li>\n<li>DeleteMethod<\/li>\n<\/ul>\n<p>These methods are available to the control as a property or an attribute that can be specified at design-time.\u00a0 With some editor auto-completion magic, Visual Studio will generate the appropriate signature for these methods in the code-behind of the page you are working on.\u00a0 Let\u2019s take a look at the GridView control demonstrated in the previous code listings and update it to use model binding for the SelectMethod:<\/p>\n<p>That looks fairly simple, as we moved the code to read the data into the myGrid_GetData method in the code behind.\u00a0 The advantage here is that all of my data access code is in one method in the code-behind and that method will be called by the page when it is needed.\u00a0 You can use a repository model with this technique, and really take advantage of that design strategy by implementing the OnCallingDataMethods event handler.\u00a0 Inside the arguments passed through this event is a property called DataMethodsObject that contains the instance of the repository object that houses the methods you want ASP.NET to call for your control.\u00a0 Let\u2019s take a look at a page with a FormView control so that we can manage the details of a trip, and add SelectMethod, InsertMethod, and UpdateMethod attributes:<\/p>\n<p>What\u2019s with the <span style=\"font-family: 'courier new', courier;\">[QueryString]<\/span> markup in the myForm_GetItem method on line 11?\u00a0 That is a hint that indicates to the ASP.NET page where it can acquire a value to pass in to the SelectMethod if it has not been explicitly called.\u00a0 In the case of this page, the id is delivered in the querystring parameter \u201cid\u201d.<\/p>\n<p>Next question: where does the id argument come from in the UpdateMethod?\u00a0 There is no querystring argument on this method, but the id is declared as a DataKeyNames value on the FormView.\u00a0 This tells ASP.NET that the primary key for the trip object is the id property and to use that in submitting updates.\u00a0 The argument name on the UpdateMethod is the same name as the name of the property in the DataKeyNames attribute in my ASPX markup.<\/p>\n<p>Last question:\u00a0 In the InsertMethod and UpdateMethod there is a call to TryUpdateModel.\u00a0 What is that method call?\u00a0 This is a helper method that will attempt to pass values that were submitted to the page into the model object that is passed into TryUpdateModel.\u00a0 This prevents that silly mapping code that you always ended up writing like this:<\/p>\n<p>Too cool! I don\u2019t have to write all of that annoying code and I can now move my data-access methods for this control to a class library or even use dependency injection to pass an interface for a concrete class:<\/p>\n<p>The major disadvantage to this approach is that it runs synchronously within the page lifecycle and you can\u2019t really take advantage of async and await methods to access the datastore.<\/p>\n<p>Until now\u2026<\/p>\n<h2><\/h2>\n<h2>Async Model Binding in ASP.NET 4.6<\/h2>\n<p>Web Forms has had the ability to run forms asynchronously since ASP.NET 3.5 back in 2008.\u00a0 However, you needed to construct your own threads and map the callback through an event to be processed by the form before the content was delivered to your visitors.\u00a0 My mind melted every time I tried to debug a page that I built with this technique.\u00a0 It worked great, but was real hard to maintain.<\/p>\n<p>With C# 5 and ASP.NET MVC 5, asynchronous programming became a lot easier to follow and build with the <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/vstudio\/hh191443(v=vs.110).aspx\">async and await<\/a> keywords and delivering Task objects.\u00a0 The promise was simple: use these keywords and the host application will release threads while awaiting for background processing.\u00a0 This makes great sense for use in database interactions, as we do not want to block web server requests while awaiting database processing.<\/p>\n<p>I can adapt the gridview to use asynchronous processing with the database in three simple steps.<\/p>\n<p><strong>Step 1:<\/strong> \u00a0Add an async=true modified to the @Page directive:<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"scroll\"><code class=\"html\">&lt;%@ Page Language=\"C#\" Async=\"true\" Title=\"My Trips\" AutoEventWireup=\"true\" CodeBehind=\"Default.aspx.cs\" \r\n Inherits=\"TravelDemo.Trips.Default\" MasterPageFile=\"~\/Site.Master\" %&gt;<\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><strong>Step 2:<\/strong> Add an async keyword to my select method and wrap the return object in a Task<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"scroll\"><code class=\"csharp\">public async Task&lt;IEnumerable&lt;Models.Trip&gt;&gt; myGrid_GetData()<\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><strong>Step 3:<\/strong> Await the appropriate async version of the data access method<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"scroll\"><code class=\"csharp\">return await ctx.Trips.OrderBy(t =&gt; t.DepartureDateTimeUtc).ToListAsync();<\/code><\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>That\u2019s it for our grid.\u00a0 The FormView and its data modification methods can be updated similarly in their repository class:<\/p>\n<h2>Summary<\/h2>\n<p>Data access has come a long way with ASP.NET, and with the latest updates we can be extremely productive with high-end multi-threading capabilities with small changes to our existing single-threaded code base.\u00a0 Unfortunately, with great code power comes great responsibility.\u00a0 If you make every database call on your application async, that means that the web server will release threads and handle more volume for you while database processing occurs.\u00a0 Cool!\u00a0 However, the database will feel the direct force of every query with no processing boundary between the web browser and the query.\u00a0 Your DBA may see traffic surge, so use these techniques wisely.<\/p>\n<p>The source code branch for this project in Visual Studio 2015 is available on GitHub.\u00a0 Take a look at the <a href=\"https:\/\/github.com\/csharpfritz\/TravelDemo\/tree\/1-Before\">before<\/a> and <a href=\"https:\/\/github.com\/csharpfritz\/TravelDemo\/tree\/1-AsyncModelBinding\">AsyncModelBinding<\/a> branches to understand how the project evolved from the 2001 data access model to the async programming model of today.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the first in a twice weekly series of blog posts that we\u2019re going to share from the Microsoft Web Development blog to showcase some of the really cool and mature features available in ASP.NET Web Forms for developers to use in their projects. \u00a0Web Forms is the development model that was deployed first [&hellip;]<\/p>\n","protected":false},"author":405,"featured_media":58792,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197],"tags":[31,7434,7500],"class_list":["post-5701","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","tag-asp-net","tag-asp-net-4-5","tag-web-forms"],"acf":[],"blog_post_summary":"<p>This is the first in a twice weekly series of blog posts that we\u2019re going to share from the Microsoft Web Development blog to showcase some of the really cool and mature features available in ASP.NET Web Forms for developers to use in their projects. \u00a0Web Forms is the development model that was deployed first [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/5701","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/405"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=5701"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/5701\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=5701"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=5701"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=5701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}