In the Lab: Amplify.ActiveRecord (porting ruby to c#)

Scott StultsJanuary 24, 2008

I’ve been secretly working on an active record type of port as a part of the Amplify Framework. The release of the ASP.Net MVC framework along with an MVC/MVP/Rails has really pushed me in that direction. So far there is both Subsonic and Castle’s MonoRail which ports a significant amount of code into .Net. However a lot of Rails like paradigms are somewhat lost as everything seems to be extremely object oriented versus using strings or even api calls. (Though MonoRail does have a very cool Inflector class which is a port of methods like pluralize & humanize, etc.

So basically I’m prototyping using Linq as the base for making queries at this point, and plan to then use the same API to do an adapter that uses ADO.Net, and hopefully this will go hand in hand with the new ASP.Net MVC framework. If you want to play with the alpha stage bits (with no warranties of code compiling at any given time as this is a prototype) then feel free to download from the projects new home.

svn checkout amplify-net-read-only

Below is code from what you might find in subsonic, which does have a very cool query object, but still takes more of a .Net approach, when using the API.

ProductCollection products = new ProductCollection().Where("categoryID",      1).OrderByAsc("listOrder").Load();
ProductCollection products = new ProductCollection().Load(new Query("Products").ExecuteReader());

So my goal is to do more of a direct port that has methods like .New(), .Create(), .Find() and using convention over things like heavy strongly typed objects. I’m probably going to make use of .Net 3.5 since the ASP.Net MVC is using 3.5. I’m also going to make these objects decorated so that you can do something like the following verses using reflection or having to set/get every property.

Products list = Product.Find(new Selection().Where("Name Like '%?'", "A").SortBy("listOrder ASC"));// sql
Product product = Product.Find(12);
Product product = Product.Find("Name = '?' AND Age = ?", "michael", 28));
Product product = Product.Find("by_Name_and_Age", "michael", 28));
var x = Product.Find(new Options().Where("Name.StartsWith(@0) || Name.StartsWith(@1)", "a", "b").SortBy("Name DESC"));  //using System.Linq.Dymanic

// in the page_load
ds.Inserted += new ObjectDataSourceStatusEventHandler(ds_Inserted);
ds.Updated += new ObjectDataSourceStatusEventHandler(ds_Updated);

void ds_Inserted(object sender, ObjectDataSourceStatusEventArgs e)     {

void ds_Updated(object sender, ObjectDataSourceStatusEventArgs e)     {
    Person person = GetPerson();
    person.Merge(e.OutputParameters); //IDictionary

    person["Name"] = "Michael"; // or
    person.Age = 28;

    person.Merge(new Hashtable() {
        {"Name", "Bob"},
        {"Age", 10}

// or in the new MVC framework
// <a href="">click here to see what MVC looks like with linq</a>
public void Create()      {
    Person.Create(Request.Form); //IDictionary
    RedirectToAction(new { Action = "Category", ID = production.CategoryID});

So you can see above that the objects have both strongly type properties and a dictionary style accessors as well so you could easily map objects using a foreach statement (which the .Merge will do) versus having to use reflection, which is also true when pulling data from a datareader.

Other goal will be to make the objects utilize Linq and replace the code that SQLMetal currently generates, so designing Linq compatibility and working on a code generator is also in the works. Since I’m using .Net 3.5, I can abuse static methods in order to make writing CodeDom a lil easier.

More blog articles:

Let's do a project together!

We provide tailored search, discovery and personalization solutions using Solr and Elasticsearch. Learn more about our service offerings