Practical ASP.NET
Integrating AJAX and a Client-Side Grid in ASP.NET MVC
Peter completes his series on integrating AJAX and a client-side control by implementing it in ASP.NET MVC. And he draws some conclusions on client-side development in ASP.NET, ASP.NET MVC, and in the world at large.
This column completes a discussion from in an
earlier column where I suggested that if, in ASP.NET MVC, you want a grid, you should use a client-side grid that interacts with Web services. In that column and the following ones, I've implemented the ExtJs grid in "traditional" ASP.NET with asmx files and with WCF services (See
JavaScript Controls with WCF and ASP.NET). To swap those technologies in and out, I had to make minor changes to my client-side code (one line) and server-side code (integrating with WCF).
In porting the application to ASP.NET MVC, many of the changes are mechanical. After creating an ASP.NET MVC application, I recreated my Entity Framework model (still the easiest way to generate my business objects) for the Northwind database. I put the class file with my data transfer object (CustomerDTO) in the Models folder. I then added the ExtJs files to my application, putting whole of the ExtJs resources and adapter folders to my project's Contents and Scripts folders. I also included the base jQuery library and put my own code in a script file:
<link href="../../Content/ExtJs/resources/css/ext-all.css"
rel="stylesheet" type="text/css" />
<script src="../../Scripts/ExtJs/adapter/ext/ext-base.js"
type="text/javascript"></script>
<script src="../../Scripts/ExtJs/ext-all.js"
type="text/javascript"></script>
<script src="../../Scripts/jquery-1.4.1.min.js"
type="text/javascript"></script>
<script src="../../Scripts/Index.js"
type="text/javascript"></script>
I added my two buttons to the form on the Index.aspx page called from HomeController. So far, no real changes other than using jQuery to attach my events to my buttons:
$(function () {
$("#btnGetCustomers").bind("click", function (e) {
return getCustomers();
});
$("#btnSaveCustomers").bind("click", function (e) {
return saveCustomers();
});
}
);
Those changes were all discretionary. But my Web service methods, rather than going in separate files, are now part of the HomeController that handles my view. Changes were definitely required here to make it work. For instance, in the method that returned customer object, I had to explicitly specify the root for my array of JSON object (before, this just defaulted to "d"):
Public Function GetCustomers() As ActionResult
Dim nw As New northwndEntities
Dim res = New With {
.d = From cust As Customer
In nw.Customers
Select New CustomerDTO With {
.ContactName = cust.ContactName,
.CustomerId = cust.CustomerID}
}
Return Json(res)
End Function
The method that accepts the JSON objects coming back from the client needed more extensive changes -- I couldn't get the results returned in a parameter to the method. Instead, I had to assemble the JSON string from the Request object's InputStream, strip out some unnecessary characters and then use the JSON serializer to convert the result into my data transfer object (the update code didn't change). I turned the conversion code into a generic method that I can use in any subsequent application:
Public Function UpdateCustomer(ByVal d As Object) As ActionResult
Using nw As New northwndEntities
Dim cDTOs As CustomerDTO()
cDTOs = ConvertJSON2DotNet(Of CustomerDTO())()
For Each cDTO As CustomerDTO In cDTOs
UpdateACustomer(nw, cDTO)
Next
nw.SaveChanges()
End Using
Return View("Index")
End Function
Function ConvertJSON2DotNet(Of T)() As T
Dim Json As New StringBuilder
For ing As Integer = 0 To Request.ContentLength - 1
Json.Append(Convert.ToChar(Request.InputStream.ReadByte()))
Next
Dim ser As New System.Web.Script.Serialization.JavaScriptSerializer()
Dim js As New JavaScriptSerializer
Json.Remove(Request.ContentLength - 1, 1)
Json.Remove(0, 5)
Return js.Deserialize(Of T)(Json.ToString)
End Function
I then had to change the URLs that my client-side code used to point to my Web service methods:
proxyCustomer = new Ext.data.HttpProxy({
api: {
read: "/Home/GetCustomers",
update: "/Home/UpdateCustomer"
},
method: "POST"
});
And with those changes, my grid displayed customers and let me do updates. The download for this week's column has the working versions of all three projects: The original using ASMX files, the second iteration using WCF, and this version using ASP.NET MVC.
One of the claims for ASP.NET MVC is that it's a better platform for JavaScript than "traditional" ASP.NET. Personally, I'm not getting that. As far as writing or debugging JavaScript code goes, I find ASP.NET and ASP.NET MVC virtually interchangeable. For this application, it was easier to use WCF than MVC Action methods. I'm not suggesting that ASP.NET MVC isn't a great technology -- I am saying that I don't see the JavaScript benefits, compared to the other options. And I realize that this one application does not a development platform make.
On the other hand, I remain excited about the power of the JavaScript and Web services paradigm. There's no doubt that using a client-side grid is more work for the developer than using a GridView and, thanks to the nature of JavaScript, harder to debug (misspell the name of a property? Not a problem: JavaScript just adds the property). We also desperately need a JavaScript MVC programming infrastructure on the client where we're creating applications whose architecture we'd sneer at on the server.
But, for me, the benefits of the AJAX paradigm in terms of responsiveness, scalability, reusability, and integration of services far out way those costs.
Anyway, that's enough client-side programming for a couple of columns, at least. For the next few columns "real" ASP.NET stuff: the listbox, Crystal Reports vs. SSRS, and back to encrypting the web.config. Then it's client-side programming with the new jQuery additions.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.