Practical ASP.NET
JavaScript Controls with WCF and ASP.NET
In my last few columns I started to integrate a JavaScript control more sophisticated than the basic jQuery UI controls into an ASP.NET page -- something on the order of the ASP.NET GridView (
Working With a Client-Side AJAX Control and
Updating Rows with a Client-side Grid).
For an article on using a jQuery UI control, see Doug Gregory's
excellent column in the November issue of Visual Studio Magazine.
My goal was to have all data retrieval and updates to be handled by calling Web services from the browser. I also wanted to do it with the least amount of code and take all the defaults.
For my first cut, I used an ASP.NET 3.5 application that pulled data from a Web service hosted in an asmx file. That almost worked for data retrieval, but my updates only worked if I was using FireFox. However, rather than fiddle with old asmx technology, I wanted to move onto WCF.
I created a new Web application in ASP.NET 4.0 (though I don't think that makes much difference). I did take advantage of some features of the .NET 4 platform: I removed underscores in my VB code, used auto-implemented properties in my data transfer object, and recreated my middle tier business objects in Entity Framework 4 Again, using Entity Framework isn't critical to this project: it just let me generate a data layer very quickly.
I copied my HTML and script from my ASP.NET 3.5 unchanged, except for changing the extensions on my services from ".asmx" to ".svc." I added an AJAX-Enabled WCF service to the project and copied my code from my asmx file changing my "WebMethod" attributes to "OperationContract" attributes. Finally, I added the ExtJs JavaScript libraries to my Scripts folder and the ExtJs CSS files to my Styles folder.
In my last column, I discovered that I had to explicitly set the content type of my requests in my ExtJs HttpProxy to indicate that I wanted JSON objects. To talk to my WCF service, I had to set a default header for all of my ExtJS requests to have them use JSON by adding this line to my client-side script:
Ext.lib.Ajax.defaultPostHeader = 'application/json';
And the song remained the same: I could retrieve objects from a Web service but not update them. For debugging this kind of problem, I use FireFox because it has the wonderful FireBug add-on for viewing requests and responses to my Web service. I also added this attribute to my WCF class to get error messages sent to the browser:
<ServiceBehavior(IncludeExceptionDetailInFaults:= True)>
Using FireFox + FireBug, I quickly discovered that my JSON objects were going to the server but instead of getting the standard JSON notation (with lots of braces) I was getting encoded text. Fortunately, I know how to turn that feature off: I set the encode option in my client-side JsonWriter to false:
writeCustomer = new Ext.data.JsonWriter({encode: false});
At that point, almost everything started working: I could retrieve multiple records, change multiple rows in the grid, and send multiple objects back to a Web service for updating. However, if I only updated a single row in the grid then my server-side code would fail because I had set up my Web service's update method to accept an array of objects. Fortunately, I knew how to fix that, also: I set the listful property on my JsonWriter to true, causing it to always send back an array of objects even if only one row had been changed in the grid:
writeCustomer = new Ext.data.JsonWriter({
encode: false,
listful: true});
The final version of my Web service update method looks like this (the "d" in the parameter is required because of the convention used to move JSON objects):
<OperationContract()>
Public Sub UpdateCustomer(ByVal d As CustomerDTO())
Dim nw As New northwndEntities
For Each cDTO As CustomerDTO In d
... code to update database...
Next
nw.SaveChanges()
End Sub
Finally, I ported my client-side changes back to my previous project to see if I could get the same results with an asmx-based Web service. The answer is, mostly, yes. Adding the listful and encoding options plus rewriting the update Web service method to accept an array of CustomerDTO objects enabled updates. Replacing the explicit headers setting in the HttpProxy with the defaultPostHeader property actually broke the code. Since including the headers setting in the proxy breaks the WCF service, you can't have one set of client-side code for both asmx and svc files -- but, then, why would you want to?
So, next column: ASP.NET MVC: which is where this whole discussion started (and some more).
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/.