Blog

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

Ive 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 Castles 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 Im 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 http://amplify-net.googlecode.com/svn/trunk/ 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. Im probably going to make use of .Net 3.5 since the ASP.Net MVC is using 3.5. Im 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"));//ado.net sqlProduct 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_loadds.Inserted += new ObjectDataSourceStatusEventHandler(ds_Inserted);ds.Updated += new ObjectDataSourceStatusEventHandler(ds_Updated);void ds_Inserted(object sender, ObjectDataSourceStatusEventArgs e)     {    list.Add(Person.Create(e.OutputParameters));}void ds_Updated(object sender, ObjectDataSourceStatusEventArgs e)     {    Person person = GetPerson();    person.Merge(e.OutputParameters); //IDictionary    person.Save();    person["Name"] = "Michael"; // or    person.Age = 28;    person.Save();    person.Merge(new Hashtable() {        {"Name", "Bob"},        {"Age", 10}    });    person.Save();}// or in the new MVC framework// click here to see what MVC looks like with linq[ControllerAction]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 Im using .Net 3.5, I can abuse static methods in order to make writing CodeDom a lil easier.