Sunday, January 15, 2012

Implementing ASP.NET MVC like NameValueCollection translation to method parameters

If you've programmed with ASP.NET MVC, you know about a neat feature where the form post data or the query string, which is string data, is automagically converted to strongly typed objects when it is passed to its Controller Action.

For instance, consider the following code:

public class HomeController : Controller
{
     public ActionResult ProductQuery(string Name, int Code)
     {
     ...
     }
}

The web request to http://<server<:port>></AppRoot>/Home/ProductQuery?Name=123&Code=456 will be properly handled and Name will be treated as a string while Code will be treated as an integer value.

Also consider the following code:

// UserRegFormData.cs
public class UserRegFormData
{
     public string Name{get;set;}
     public DateTime Dob{get;set;}
}

// HomeController.cs
public ActionResult Register(UserRegFormData urfd)
{
...
}

You could now setup an html form with action=/Home/Register, method=post, a text field with name="urfd.Name" and another one with name="urfd.Dob". When this form is submitted with data in the two text fields, ASP.NET MVC will automatically create the strongly typed UserRegFormData object with correct values for its properties. This wasn't the case before ASP.NET MVC. You would receive all the values as strings and you were responsible for doing the conversions in your ASP.NET server side code.
Well I've been thinking about how this could be done without using ASP.NET MVC.

Why reinvent the wheel, you might ask. The answer is two fold. Firstly, there might be situations where ASP.NET MVC might just not be a suitable solution, a non web application, perhaps. This type of routing and smart parameter translation would also be useful in scenarios where input contains both the parameters as well as the operation the perform on them. Secondly, I have been facinated by this very approach ever since I discovered it in ASP.NET MVC. When I recently came across Managed Extensibility Framework (MEF) as it is packaged with .Net 4, I wondered if it could be used to create an MVC framework from scratch. It would be a good learning opportunity and I wanted to cease it.

While I made some progress with routing URLs to methods in controller objects, I got faced with the issue of handling the input. The input data would not translate itself to strongly typed objects. Few searches on the Internet revealed that there was nothing readily available for this and I really didn't want to dig through the ASP.NET MVC code to see how it was implemented there.

I did an Internet search on how to convert string objects to other object types. This StackOverflow article provided the solution:

Another search on how to assign property values using reflection yeilded this post on DotNetSpider:

I found how to create strongly typed arrays from this article on Byte.com:

I had most of what I needed to get started.

I have uploaded the initial code to my github repository. The code is still crude and it has not yet undergone a refactoring exercise. https://github.com/jimmy00784/MEFExample/tree/master/StringToArgumentsTest.

Monday, January 9, 2012

RavenDB on Linux - Update

I spent some more time with RavenDB source code trying to figure out what might have been causing the runtime errors which had led me to comment the "SatisfyImportsOnce" line and supply code to manually load MEF exports.

It turns out that some of the Imports were not being satisfied. The one place in particular was in the OAuth code under Raven.Database/Server/Security/OAuth/OAuthClientCredentialsTokenResponder.cs file. The member IAuthenticateClient AuthenticateClient was expecting import of type IAuthenticateClient which was not being satisfied.

I reverted my changes made to in connection with disabling SatisfyImportsOnce and loading exports manually, rebuilt the project, and fired up the server application. I was presented with the same nasty stack trace.
I commented out the Import attribute from AuthenticateClient, rebuilt the project, and tried running the server second time. It worked!

There were other similar instances in the code where the imports were being satisfied with corresponding exports. I learnt this from running the xUnit tests on the application. It wasn't making sense. RavenDB was supposed to be a complete solution.

I did a filesystem search for AuthenticateClient under solutions root folder and sure enough I found results in CSharp code files that were not part of the Raven.sln file. These files and many more were under the Bundles folder under their own solution. I compiled these projects - Raven.Bundles.Tests did not build due to some issues - mono or monodevelop specific I assume.

I copied the generated dll files into the Bundles/Plugins folder and set its path as the value to the "Raven/PluginsDirectory" key in App.Config for Raven.Server project.
I uncommented the import attribute, rebuilt the solution and fired up the server the third time. It worked this time as well.

Next, I'll try to re-run some of those xUnit tests that had failed earlier to see how much ground could be covered out of those 1160 tests that came packaged with RavenDB.


This article is part of the series NoSQL - RavenDB on Linux. The series contains the following articles:
NoSQL - RavenDB on Linux
Open Source Shines - RavenDB on Linux
RavenDB on Linux - Source Code
RavenDB on Linux - Update

Tuesday, January 3, 2012

It's 2012!!!

Have a Happy, Prosperous, and an Open-Source New Year!!!
OK, that last bit doesn't make any sense, but you get it, don't you :)