How I Use SubSonic, Part 2

In Part 1, I covered building a ControllerBase class to use with SubSonic 2.1’s new RepositoryRecord entity base class. In order to get the hybrid API (RepostoryRecord + Foreign Key lazy-loaded properties) I like, I had to make some changes to the code generation templates. Here’s what I do.

First off, go into Program Files\SubSonic 2.1 Final\src\CodeGeneration\Templates and copy the CS_ templates to a new folder. I put them in the root of my project so that I can keep them in under version control. Then, in your app.config or web.config, add the following to make sure that future generations will use the modified copy of the templates:

   1:  <SubSonicService defaultProvider="NorthwindProvider"
   2:                   templateDirectory="C:\Full_Path_To\SubSonicTemplates">
   3:      <providers>
   4:          <clear/>
   5:          <add ... />
   6:      </providers>
   7:  </SubSonicService>

Another thing you should know before we continue is an important naming convention I make sure to use for foreign key fields. My foreign key fields ALWAYS end in ‘ID’. That’s pretty standard. But when I have a table that has multiple foreign key fields that relate to the same table, I make sure the field name represents what the relation is, even if the field name doesn’t match the foreign table name. For example, my goal with this is to be able to do this:

   1:  // News.Author is a User object
   2:  string authorName = News.Author.DisplayName;
   3:  // News.Editor is also a User object
   4:  string editorName = News.Editor.DisplayName;

The default SubSonic templates don’t handle this situation very well. The first FK it encounters is named News.User and the second gets a funky name I don’t even recall. It can be very confusing to use. Since I always make sure my columns are named a specific way, I decided that I wanted my property names to just lop off the ‘ID’ suffix and use whatever was left. Since a table can’t have two columns with the same name, I never have naming conflicts. Updating the CS_ClassTemplate.aspx template to handle this is straightforward:

   1:  // replace this section
   2:  string fkClass = fk.ClassName;
   3:  string fkClassQualified = provider.GeneratedNamespace + "." + fkClass;
   4:  string fkMethod = fk.ClassName;
   5:  string fkID = tbl.GetColumn(fk.ColumnName).PropertyName;
   6:  string fkColumnID = fk.ColumnName;
   7:   
   8:  // with this section
   9:  string fkClass = fk.ClassName;
  10:  string fkClassQualified = provider.GeneratedNamespace + "." + fkClass;
  11:  string fkID = tbl.GetColumn(fk.ColumnName).PropertyName;
  12:  string fkMethod = fkID.Substring(0, fkID.Length - 2);
  13:  string fkColumnID = fk.ColumnName;

There’s a few code blocks that immediately follow this to check for relations to itself and duplicate method/property names. I remove those checks because my naming convention never allow for those conflicts to exist.

Because I’m using the RepositoryRecord base class, when these FK properties are called, I need to make sure they call the .Get method on the correct controller. Again, SubSonic makes this easy.

Replace this section:

   1:  /// <summary>
   2:  /// Returns a <%=fkClass%> ActiveRecord object related to this <%=className%>
   3:  /// 
   4:  /// </summary>
   5:  public <%=fkClassQualified%> <%=fkMethod%>
   6:  {
   7:      get { return <%=fkClassQualified%>.FetchByID(this.<%=fkID%>); }
   8:      set { SetColumnValue("<%=fkColumnID%>", value.<%=fkTbl.PrimaryKey.PropertyName%>); }
   9:  }

With this:

   1:  /// <summary>
   2:  /// Returns a <%=fkClass%> ActiveRecord object related to this <%=className%>
   3:  /// 
   4:  /// </summary>
   5:  protected <%=fkClassQualified%> m_<%=fkMethod%> = null;
   6:  public <%=fkClassQualified%> <%=fkMethod%>
   7:  {
   8:  <% if (baseClass != "ActiveRecord") { %>
   9:      get { 
  10:          if (m_<%=fkMethod%> == null)
  11:               m_<%=fkMethod%> = <%=fkClassQualified%>Controller.Get(this.<%=fkID%>); 
  12:          return m_<%=fkMethod%>;
  13:      }
  14:  <% } else { %>
  15:      get { return <%=fkClassQualified%>.FetchByID(this.<%=fkID%>); }
  16:  <% } %>
  17:      set { SetColumnValue("<%=fkColumnID%>", value.<%=fkTbl.PrimaryKey.PropertyName%>); }
  18:  }

Not only are we now using the Controller to retrieve the item, we’re also storing it in a member variable so every time we call that property for this instance, we only hit the database once.

The last change you need to make to CS_ClassTemplate.aspx is to remove the checks for ActiveRecord so that foreign key properties are still generated even if you select RepositoryRecord. Download the files at the end of the post to see this in action.

There’s also a small change that needs to be added to CS_StructsTemplate.aspx to support my ControllerBase.Delete(int id) customization. Add this between the existing Delete and Destroy methods:

   1:  public static void Delete<T>(int keyId) where T : RepositoryRecord<T>, new() 
   2:  {
   3:      Repository.Delete<T>(keyId);
   4:  }
   5:  public static void Destroy<T>(int keyId) where T : RepositoryRecord<T>, new() 
   6:  {
   7:      Repository.Destroy<T>(keyId);
   8:  }

If you use GUIDs or something else for your key, you’ll have to adjust accordingly (HOWEVER, I am working on committing some new methods to the SubSonic core to allow deleting by key for all data types…stay tuned).

To use these customizations in your own projects, download my templates here.

Coming soon is Part 3 which will cover using the ControllerBase to easily add request-length (or longer) caching and query performance auditing. If you have any comments or feedback, I’d love to hear it below!

Posted October 26th, 5:46 PM
Read more posts about .NET, SubSonic, Tips.

View Comments
Link

Screencast: Setting up Windows on Amazon EC2

I recorded a short screencast on how to set up a Windows instance on Amazon EC2. I didn’t use a script so I stutter a little bit, so I apologize for that. It’s 6:30 long and weighs in at about 14.5MB.

View The Screencast

kick it on DotNetKicks.com

Posted October 23rd, 8:01 PM
Read more posts about Technology, Tips.

View Comments
Link

How I Use SubSonic, Part 1

I started heavily using SubSonic right around the time Rob Conery released his poorly-named ‘MVC Templates’. I was never a fan of the ActiveRecord pattern, but I really liked the Repository pattern used in the MVC Templates. The controllers generated by these templates were partial classes that inherited a base class that offered some common methods like List, Get, Delete, Save, etc. For each project, I would build up a pretty substantial set of controllers and I would have all my data access code in a nice centralized library.

The only place I made any real changes was with the templates themselves. Rob’s shipping version didn’t include lazy-loaded properties or methods for foreign keys like ActiveRecord. I really liked this feature, so I added them back to the templates using the controllers’ Get methods to call the correct item.

This worked great for awhile until I wanted to extend the base class. Despite repeatedly asking Rob for the source to his unofficial branch (and never getting an email back), I was stuck with what was there for awhile. Then along came SubSonic 2.1 and the new RepositoryRecord base class.

I really wanted to use the new SqlQuery capabilities so off I went to migrate my existing Controllers. In Part 1, I’ll cover recreating the ControllerBase class. In Part 2, I’ll outline my changes to the code gen templates. In Part 3, I’ll cover some caching and query metrics this architecture let me easily add.

This is the class definition I needed to replicate:

Old SubSonic ActiveController

The methods I used most frequently were:

  • Delete(object keyID): In my controllers, I would add a Delete(ItemType item) method, handle any pre-delete logic there, and then call Delete(keyID) within my method.
  • Get(object key): This method was called extensively in my generated objects when loading foreign key properties. Also used whenever I need a single item.
  • List(): Rarely used since it’s the equivalent of "SELECT * FROM Table" which isn’t very useful except for in my utility classes. Still used however.
  • List(IDataReader rdr): I used this with my StoredProcedures to get a strongly-typed collection from the results of calling the sprocs.
  • List(Query q): All other non-sproc collection loading was done here by passing a Query object from my Controllers.
  • Save(ItemType item, string, userName): In my controllers, I would add a Save(ItemType item) method overload, handle all the saving logic and validation, then call this method when I needed to finalize the changes. (Yes, this ended up with a lot of business-logic leaking into my DAL, but that was my fault, not SubSonic’s and it is currently being remedied).

My new ControllerBase needed to support all of the above scenarios and work with the new SqlQuery object.

I started with the easiest methods to replicate:

   1:  public class ControllerBase<ItemType, ListType>
   2:      where ItemType : RepositoryRecord<ItemType>, new()
   3:      where ListType : RepositoryList<ItemType, ListType>, new()
   4:  {
   5:      public static ListType List()
   6:      {
   7:          return DB.Select().From<ItemType>().ExecuteAsCollection<ListType>();
   8:      }
   9:   
  10:      public static void Delete(int id)
  11:      {
  12:          DB.Delete<ItemType>(id);
  13:      }
  14:   
  15:      public static void Save(ItemType item, string username)
  16:      {
  17:          DB.Save(item, username);
  18:      }
  19:  }

SubSonic pros will know there’s no DB.Delete(int id) method in SubSonicRepository. You’ll have to download the source and add it yourself if you want to replicate this solution exactly. Here’s the code to add to SubSonicRepository.cs (you’ll also need to update ISubSonicRepository):

   1:  public void Delete<T>(int itemId) where T : RepositoryRecord<T>, new()
   2:  {
   3:      T item = new T();
   4:      TableSchema.Table tbl = item.GetSchema();
   5:      string pkColumn = tbl.PrimaryKey.ColumnName;
   6:      Delete<T>(pkColumn, itemId);
   7:  }

OK, back to ControllerBase. Next up was replicating the List overloads for building a collection from a Query object and a Stored Procedure.

   1:  public static ListType List(Query query)
   2:  {
   3:      ListType coll = GetCollectionFromReader(query.ExecuteReader());
   4:      return coll;
   5:  }
   6:   
   7:  public static ListType List(StoredProcedure s)
   8:  {
   9:      ListType coll = GetCollectionFromReader(s.GetReader());
  10:      return coll;
  11:  }
  12:   
  13:  protected static ListType GetCollectionFromReader(IDataReader rdr)
  14:  {
  15:      ListType coll = new ListType();
  16:      coll.LoadAndCloseReader(rdr);
  17:      return coll;
  18:  }

And the Get method:

   1:  public static ItemType Get(object id)
   2:  {
   3:      if (id == null)
   4:          return default(ItemType);
   5:   
   6:      ItemType item = DB.Get<ItemType>(id);
   7:   
   8:      return item;
   9:  }

Supporting the new SqlQuery object was trivial:

   1:  public static ListType List(SqlQuery q)
   2:  {
   3:      ListType coll = q.ExecuteAsCollection<ListType>();
   4:      return coll;
   5:  }

That was all I needed for backwards compatibility. I then added a new method with overloads for Query, SqlQuery and StoredProcedure to return a single item (thanks to Rob for responding to my feature request in the forums and implementing ExecuteSingle<T>()):

   1:  public static ItemType GetFirst(SqlQuery q)
   2:  {
   3:      return q.ExecuteSingle<ItemType>();
   4:  }
   5:   
   6:  public static ItemType GetFirst(Query q) // also created an overload for StoredProcedure
   7:  {
   8:      ListType coll = List(q);
   9:   
  10:      if (coll.Count == 0)
  11:      {
  12:          return default(ItemType);
  13:      }
  14:      else
  15:      {
  16:          return coll[0];
  17:      }
  18:  }

And there you have it. A backwards-compatible controller base class that lets you use the new features of SubSonic 2.1. You can download the complete class for use in your own projects. Part 2 (hopefully tomorrow) will cover my SubSonic template customizations.

Posted October 22nd, 10:54 PM
Read more posts about .NET, SubSonic.

View Comments
Link

Pluggable ASP.NET CacheManager

I wanted a single, common method for caching items for either the length of a request (in HttpContext.Current.Items) or longer (in HttpContext.Current.Cache). Here’s what I came up with.

There’s three common operations I use for caching: storing an item, removing an item and retrieving an item. I started by defining these methods in an interface:

public interface ICacheProvider
{
    void Store(string key, object data);
    void Destroy(string key);
    T Get<T>(string key);
}

Next up was my CacheManager class. I use this as a common interface to whatever ICacheProvider I want to use in each situation. Whenever you instantiate a CacheManager, you have to specify which provider you want to use, so I put that in the constructor. Then I implemented each of the common methods.

public class CacheManager
{
    protected ICacheProvider _repository;
    public CacheManager(ICacheProvider repository)
    {
        _repository = repository;
    }

    public void Store(string key, object data)
    {
        _repository.Store(key, data);
    }

    public void Destroy(string key)
    {
        _repository.Destroy(key);
    }

    public T Get<T>(string key)
    {
        return _repository.Get<T>(key);
    }
}

Right now, I only ever store cached items in two places: HttpContext.Current.Items for items that only need to live as long as a single request and HttpContext.Current.Cache for items that I need to live across requests (like a dynamically-generated CSS style sheet). So I wrote a ICacheProvider for each of these situations:

public class RequestProvider : ICacheProvider
{
    public void Store(string key, object data)
    {
        HttpContext.Current.Items.Add(key, data);
    }

    public void Destroy(string key)
    {
        HttpContext.Current.Items.Remove(key);
    }

    public T Get<T>(string key)
    {
        T item = default(T);
        if (HttpContext.Current.Items[key] != null)
        {
            item = (T)HttpContext.Current.Items[key];
        }
        return item;
    }
}

public class ShortTermProvider : ICacheProvider
{
    public void Store(string key, object data)
    {
        HttpContext.Current.Cache.Insert(key, data);
    }

    public void Destroy(string key)
    {
        HttpContext.Current.Cache.Remove(key);
    }

    public T Get<T>(string key)
    {
        T item = default(T);
        if (HttpContext.Current.Cache[key] != null)
        {
            item = (T)HttpContext.Current.Cache[key];
        }
        return item;
    }
}

For the ShortTermProvider you can specify additional options like absolute or sliding expirations using one of the other overloads on the Insert method.

Now I can easily and succinctly cache items in the desired location:

CacheManager cache = new CacheManager(new ShortTermProvider());
cache.Store("foo", "any-object-here");
string result = cache.Get<string>("foo");
cache.Destroy("foo");

Woot! In the future, if I ever need to implement memcached or Velocity, I can just write a new provider and plug it in.

kick it on DotNetKicks.com

Posted October 7th, 9:23 PM
Read more posts about .NET.

View Comments
Link