Playing with SQLite, SubSonic3 and Repository Mode

Okay, lemme tell ya… I really, really like SubSonic right now.  I’m building a basic data integration application, and I wanted a simple way to store some basic tracking data locally. I didn’t want to build it into a Sql Server or create a super complicated data layer, so I decided to give SubSonic3’s Repository Mode with SQLite a try. SQLite gives me a simple, zero-config, file-based database solution for the app, and SubSonic provides a nice, clean way to push and pull data with it.

First of all, if you haven’t seen Conery’s five minute demo video on repository mode, you should. It’s very cool.  Once you watch it, you’ll get the basics of migration and understand the idea of building the object model first while using the database purely as a storage mechanism. In this case, I built my class, and I’m letting Migration handle all the schema generation for me. It will also create a new database file if one doesn’t already exist. That’s the idea right?

Setup Your Configuration File

For starters, don’t forget to download and install the System.Data.SQLite library first. I ran their Windows installer and it worked just fine. Then add a connection string to your config file. Be sure to set the ProviderName to  System.Data.SQLite. SubSonic uses the 2.0 provider model to determine the types required for the data connection.

<connectionStrings>  
    <add name="TrackerConnectionString" connectionString="Data Source=Tracker.db3;" providerName="System.Data.SQLite"/>
</connectionStrings>

Create Your Data Classes

Create your storage classes. If you’re new to ORM/data layers, remember that each class definition represents a row in the database. In this case, I want a table called DSTFileTrackers with three columns: ID, Filename, and Imported. These are simple classes (other than the subsonic string length attribute). They don’t inherit from any other base class. A nice attribute not used here would be the [SubSonicIgnore] attribute. It will allow you to define other properties that you don’t want to store to the database like for example, read only properties.

public class DSTFileTracker  
{
    public long ID { get; set; }

    [SubSonic.SqlGeneration.Schema.SubSonicStringLength(100)]
    public string Filename { get; set; }

    public bool Imported { get; set; }
}

Write Your Implementation

Lately, I’ve been playing a lot with ASP.NET MVC, I’ve been creating repository classes for my data objects.  They’re simple subject-matter classes that just handle business/data operations for a set of objects. In this case, I created one called TrackerRepository and implemented most of my SubSonic code there. Later on, I could build an interface for the repository and then set it up for unit testing or make it adaptable to switch to another data layer.

So here’s a sample calling code.

var tracker = _Repo.DSTFiles.Where(o => o.Filename == file.name).FirstOrDefault();  
if (tracker == null)  
{
    tracker = new DSTFileTracker();
    tracker.Filename = file.name;
    _Repo.SaveDSTFileTracker(tracker);
}

This save function below lives in my repository class. You can see how nice and clean these statements are. There’s a ton of potential here. Repository mode can do a lot of really cool stuff like deleting many records at once by expression or doing partial inserts and updates.

public void SaveDSTFileTracker(DSTFileTracker data)  
{
    if (_Repo.Exists<DSTFileTracker>(o => o.ID == data.ID))
        _Repo.Update<DSTFileTracker>(data);
    else
        _Repo.Add<DSTFileTracker>(data);
}

So all in all it was pretty easy to do. You’ll notice I ran an existence check. The SimpleRepository doesn’t have this built into a save function like ActiveRecord, but it’s not a big deal to do manually.

Catching SubSonic Gotchas

This library is new and like all new libraries, you’ll run into a bug here and there. Hopefully as more and more people use it, the SubSonic community will become more aware of the bugs out there and can fix them.  I ran into a few myself just while doing this. These are mostly specific to the SQLite portion of SubSonic. They were all very easy to fix, and it’s very easy to submit your own patches. Checkout their GitHub demo video hereYou can access my branch directly for the fixed code. I've applied for a code pull, but it takes a few days and code review before they plug it into the main.

Update August 16th, 2010

I've recently beeing using SubSonic post 3.0.0.4 version with SQLite and MySql, and most of these issues have been resolved. I pulled down my version of the repository since it's very old. I highly recommened upgrading to the latest one, which should not require any code changes. Check out one of my more recent posts that discusses SubSonic and Mono

Here’s a few of the issues I ran into:

  • SQLite’s auto-incrementing integer can only be 64bit [integer] rather than 32bit [int]. SubSonic will go and create the 64bit field during migration though; you just won’t see an error until you try to read it back. So if your ID property is an [int], change it to a [long].
  • The default code for [bool] creates a tinyint field in SQLite.  I had to tweak the Subsonic code by changing it to use a bit.  When reading the values, it breaks because tinyint == Byte and SubSonic sets values to data properties by casting them directly to their actual type in the reader.
  • When running an Update<T>, I get a NullReferenceException.  This is a bug in SubSonic and already has an issue posted. I submitted a patch for this.  It’s a simple column name mismatch in the update script generator.
  • Just like in Linq to Sql when you do custom Lambda expressions with unrelated fields, it could cause problems. I was doing a funky Where(o=>o.Equals(y)) which blew up my stuff because the LINQ predicate expression couldn’t figure out how to parse Equals() and CompareTo() to SQLite syntax.  So keep it simple. :)