4/17/2008

ActiveResource

While I was driving home from work today I was thinking about how easy it would be enhance the Dream/Subsonic stuff I've been working with and write a custom proxy-class generator for c#. I was thinking "You know... if I did it right, it would look a lot like the active-record pattern. Wonder what I should call it... 'ActiveREST'?"

Well, a quick search turned up the Rails 'Active Resource' pattern ;). I'm glad someone's already doing it, it proves that this may indeed be a useful pattern to have. I'll have to start digging into Active Resource more... wish I would have found this two weeks ago, when I started this little experiment.

4/16/2008

Dream CRUD ;)

Background
For the last two weeks or so, I have playing with the MindTouch Dream framework (.NET REST) and the new Subsonic dynamic query generator. Initially the idea was to create one or two quick and dirty APIs that allow me to perform basic CRUD operations on resources that I will be storing in a database. I did write these one or two APIs, and pretty soon I realized that this access pattern was fairly powerful. It does not take full advantage of all that REST has to offer, for example it only allows XML formatted payloads, but if it is used correctly I think there is a lot I could gain from this access pattern. So I decided to broaden the scope a bit and create some base functionality that would allow me to leverage this access pattern in a more generic fashion.

Result
Essentially, I've created a way to selectively expose some of my database resources via REST. The whole idea revolves around using Dream's performant and easy to use XDoc class as a DTO wrapper around XML resources. Then I use Subsonic to generate clean, dynamic queries that will execute against any of Subsonic's supported dataproviders; a list that includes MySql, MS Sql, and Oracle.

Here's an example CRUD method, which retrieves a row of data from a table by a specified key name/value pair.


protected static DreamMessage GetTableAsMessageByKey(
string rootNodeTag,
string tableName,
string keyColumn,
object keyValue)
{
DreamMessage message;
XDoc doc;

try
{
if (rootNodeTag == null) throw new ArgumentNullException("rootNodeTag");
if (tableName == null) throw new ArgumentNullException("tableName");
if (keyColumn == null) throw new ArgumentNullException("keyColumn");
if (keyValue == null) throw new ArgumentNullException("keyValue");

doc = new Select().From(tableName).Where(keyColumn).IsEqualTo(keyValue)
.ExecuteXDoc(keyColumn, rootNodeTag, null);

if ((doc == null) || (doc.Count() < 1))
throw new NotFoundException("{3ED0B0F0-2C92-49e1-B710-1CC30CC2F769}",
Resources.DATA_ID_NOT_FOUND,
new[] { rootNodeTag, keyColumn, keyValue });

message = DreamMessage.Ok(doc);
}
catch (Exception ex)
{
message = HandleException("{4FCDA723-C0E6-4316-8F0F-3A9D20484C08}",
string.Format("GetTableMessageById(rootNodeTag={0}, tableName={1}, " +
"keyColumn={2}, keyValue={3})", rootNodeTag ?? "", tableName ?? "",
keyColumn ?? "", keyValue ?? ""), ex);
}

return message;
}


The SubSonic Select() query statement I use ends with ExecuteXDoc() which is a .NET 3.5 extension that I added to perform an IDataReader -> XDoc mapping.

I've also added some of pluming that's specific to my needs. The NotFoundException() will cause a DreamMessage with a 404 (not found) HTTP status code to be created and return to the client; the title and message of the response will be localized to match the client's language specification. The HandleException() method takes care of DreamMessage creation, and it writes a log entry using the log4net logger.

Here's an example GET method declaration that uses this generic mapping pattern.


[DreamFeature("GET:{lang}/modules/{id}", "Get modules.")]
[DreamFeatureParam("{lang}", "string", "The requestors preferred language")]
[DreamFeatureParam("{id}", "int", "The id of the module to get")]
public Yield GetModuleById(
DreamContext context,
DreamMessage request,
Result<DreamMessage> response)
{
string lang = context.GetParam<string>("lang");
int id = context.GetParam<int>("id");

try
{
SetCurrentCulture(lang);
response.Return(GetTableAsMessageByKey("Module", "core_module", "id", id));
}
catch (Exception ex)
{
response.Return(HandleException(
"{AB360F60-EDF0-4734-BA53-99A38C220664}",
string.Format("GetModuleById(lang={0}, moduleId={1}",
lang, id), ex));
}
finally
{
ResetCurrentCulture();
}

yield break;
}


And a response generated from a GET to '/en-US/modules/2'


<Module xml:id="2">
<create_date>2008-04-01T04:12:52Z</create_date>
<modified_date>2008-04-01T04:12:52Z</modified_date>
<modified_user>1</modified_user>
<version>1</version>
<provides_namespace>tempuri</provides_namespace>
<base_uri>tempuri/base</base_uri>
<active>true</active>
</Module>


Benefits
This access pattern provides several benefits.
  • Performance - Because I am not using any intermediate DTO classes or business objects, I don't have to go through the entire "deserialize request -> retrieve data -> map data to object -> serialize object as response" lifecycle. this solution is very performant.
  • Database Agnostic - SubSonic allows me to talk to just about any database I would be interested in supporting, and the query format will not change based on the database I am using.
  • Customizable - I'm in control of any special processing I want to do. I'm already returning application-specific localized messages and doing custom logging on the server-side. In the future I want to be able to perform authorization based restrictions on select operations. This solution allows me to do all of that and more.
  • Easy - with a full set of CRUD operations already in place, I'm able to crank out access to my database-centric resources in minutes.
  • Consistent - all of the resources I expose using this pattern will be formatted in the same, predictable format, which makes it easier for clients to consume. For example, I'm using the 'xml:id' attribute to identify the object's key field. Because I'm also enforcing consistency in my database schema (naming conventions, common set of required fields, etc) a certain level of consistency trickles back to the client by default.
Limitations
Obviously there are several limitations. This implementation forces you to expose your database schema as your payload, which is not desirable in many situations. Additionally, the ability to perform complex validation or business logic spanning more than one resource type is difficult.

But even with these limitations in mind, I know this pattern is going to help me ease the pain of implementing the 20% or more CRUD-only operations I run into every day when creating enterprise-level applications.

4/07/2008

Interlocked Class

Sometimes you'll be digging through some code and run across a system class or method that you've never seen before. Today I found this gem.


Interlocked.Increment(ref safeInstanceCount);

The Interlocked class provides a simple and elegant way to perform thread-safe atomic increment, decrement and add operations.

Free Learning from Microsoft

Free E-Learning
Clinic 6262: Introducing Windows Workflow Foundation using .Net Framework 3.5 & Visual Studio 2008
Clinic 6263: Introducing Windows Presentation Foundation using .Net Framework 3.5 & Visual Studio 2008
Clinic 6264: Introducing Windows Communication Foundation using .Net Framework 3.5 & Visual Studio 2008
Free E-Book (MS Press)
Introducing Microsoft LINQ by Paolo Pialorsi and Marco Russo
Introducing Microsoft ASP.NET AJAX by Dino Esposito
Introducing Microsoft Silverlight 1.0 by Laurence Moroney
Enjoy!

4/06/2008

Super-Simple REST

It doesn't get much easier than this...

I'm using Mindtouch Dream as a REST host & SubSonic as a data access layer. I wrote a helper class to convert the
IDataReader results to an XDoc (Mindtouch's lightweight XMLDocument object similar to MS's new XDocument).
Keep in mind that this is an ActiveRecord pattern, so I am only returning the results of a single table.

 
[DreamFeature("GET:modules/{id}", "Get modules.")]
public Yield GetModuleById(DreamContext context, DreamMessage request, Result response)
{
XDoc doc;
int id = context.GetParam("id");

using (IDataReader reader = new Select().From("core_module").Where("id").IsEqualTo(id).ExecuteReader())
doc = XDocHelper.GetXDoc(reader, "Module", true, "BaseUri", "create_date");

response.Return(DreamMessage.Ok(doc));

yield break;
}

The helper class is smart enough to convert database columns ("modified_date") to camelcase names ("ModifiedDate").
And I added an option to allow you to specify which columns should be XML Attributes. The method signature is:
 
public static XDoc GetXDoc(IDataReader reader, string rootNodeTag, string itemNodeTag, bool useCamelCase, params string[] attributeColumns)

In this example, I told it to use camelcase, and both column names "BaseUri" and "create_date" should be attributes:

doc = XDocHelper.GetXDoc(reader, "Module", true, "BaseUri", "create_date");

This results in an XDoc created with the following Xml


1
2008-04-01T04:12:52Z
1
none
tempuri
true

4/05/2008

Subsonic

Subsonic - this is pretty cool stuff and it seems to have matured quite a bit in the last year-or-so.

DB Independent dynamic query generation

  • Similar to LINQ for SQL, but it's DB-independent
  • Pagination support
  • Results as DataReader / DataSet / Scalar
  • Params are used to aviod SQL Injection
SPROC code gen
  • Generates classes to call sprocs in a type-safe manner
ActiveRecord
  • Implementation of the Active Record pattern
  • Code is generated from DB tables or views, so reflection performance hits should be minimal
  • Does not rely on third party O/R mapper, which is different from the Castle + NHibernate active record implementation.
Definitely not as flexible as LLBLGen Pro, but free & great if you want to work with dynamic queries and DataReaders instead of full-blown entity objects.

Useful Powershell Scripts

Remove SVN Bindings

Get-ChildItem -Include .svn -Recurse -Force | ForEach-Object { del $_.FullName -Recurse -Force }

Remove bin and obj dirs

Get-ChildItem -Include obj,bin -Recurse | ForEach-Object { del $_.FullName -Recurse -Force }

Prepend mpl.txt to all *.cs files below the current path

Get-ChildItem -include *.cs -Recurse | ForEach-Object { copy C:\mpl.txt $_.Name; type $_ | out-file $_.Name -append -encoding Ascii; move $_.Name $_.FullName -force }