About

SubSonic is a .net open source project developed by Rob Conery and a core team of developers including Eric Kemp, Scott Watermasysk, Jon Galloway, Phil Haack, and Gavin Joyce. The current stable release is version 2.0.3. Nightly builds are available in our SVN respository.

Tags

Using Active Record

SubSonic creates objects for your which reflect your database structure. This is generally called Object-Relational Mapping (OR/M) and while it's true that SubSonic is an OR/M tool of sorts, it does not go the way of normal OR/M tools. You don't have to abide by our rules, never use SPs, etc. You do what you do and we try to make it easier for you.

 

Tables

Every table in your database (or the ones you specify in the abp file) is created as an object by SubSonic, which inherets from the ActiveRecord base class. These objects allow you to push/pull data from your database pretty easily:

Product p = new Product(1);

Product p = new Product("ProductGUID", "02020-29929-ksk2k-kksksk-anhu3");



In addition to the core classes, strongly-typed collections are also generated for you:

//loads a collection of products with a category ID of 1

ProductCollection products = new ProductCollection().Where("categoryID", 1).Load();

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



Note
You can load a collection using a reader as well:

ProductCollection products=new ProductCollection().Load(new Query("Products").ExecuteReader());

But doing it this way WILL NOT CLOSE OFF THE READER. This can obviously lead to performance problems. Why you ask? In general, code that calls the reader should close the reader. We wanted to be sure we supported this so we don't close the reader on load of the collections.

You can iterate over the collection easily:

ProductCollection products=new ProductCollection().Where("categoryID",1).Load();

foreach(Product product in products){

    //...

}



Saving Data

Each object is aware of it's origins - this is the core of ActiveRecord. Each object represents one row in your database, so when you want to save your data, you can simply:

Product p=new Product(1)

p.Name="Changed Name";

p.Save(User.Identity.Name);  



The last line with the save method is one of our conventions - passing in who did what when. You can override this if you like.
The Save() method takes a look at an internal state variable called "IsNew". This variable tells the object whether to run an Update() or Insert().

Object Data Source Support

You can use the ODS with SubSonic by using each object's Insert() and Update(), and FetchAll() methods. These are static methods on each class generated for your table.

Deleting Data

If you are using logical deletes - delete flags instead of record removal - SubSonic will support this. Our convention is to use "Deleted" or "IsDeleted" as the flag for this column, and that it's a bit field. If you have these fields in your database SubSonic will mark it appropriately on delete, or it will just delete the record.

You can use a permanent delete along with the logical deletes by using "Destroy()".

These methods are static to each object.

Views

Views are supported as read-only objects in SubSonic. You can't update or save information from a view, but these objects are super handy when you have complex data structures. Querying them is exactly the same as with tables, and you have full strongly-typed collection support as well.

Stored Procedures

All stored procedures are wrapped in a single class, each with the their own method. The class is by default called "SPs" and returns a type of SubSonic.StoredProcudure. So, to use your SP:

//for a passthrough

SPs.MySPName(args...).Execute();

 

//returns a reader

IDataReader rdr=SPs.MySPName(args).GetReader();



You can alse use ExecuteScalar() or GetDataSet(). If you don't like the name SPs, you can change this in the configuration:

spClassName="MySPs";
#1 Kevin on 3.27.2008 at 1:54 AM

While I don't know much about SubSonic yet, I do like what I see so far. My only concern/issue is with how stored procedures are handled. Fundamentally there is nothing wrong with using a utility method approach, but what I want is a way to associate the proc with a specific table/class so that the class would be able to generate the appropriate execute/load methods.

To elaborate on what I'm talking about, it would be nice to call a method, say something like ExecuteGetProducts(), that is on par with that of the Where() method. The method would execute the stored procedure instead of creating dynamic SQL and when coupled with the Load() method would also hydrate the object with the resulting data. Of course you open yourself to issues when the stored procedure does not return the required number of fields, but I'm sure that could be dealt with in a reasonable way. The call could look something like ProductCollection pc = new ProductCollection().ExecuteGetProducts(new {Category=5}).Load() or productInstance.ExecuteUpdateProduct(). I guess at the end of the day I would like to more closely associate the stored procedures with the individual classes.

Also, In addition to what I perceive to be a poor programming experience, I'm not sure the utility method approach is practical if you have more than few stored procedures. At the company I work with we are required to use stored procedures which means most of our databases have hundreds if not thousands of stored procedures.

Subscribe