This wasn’t specifically Silverlight-related, but you really can’t have much of a Silverlight application without some kind of data access.  We chose WCF services and built up some quick models with NHibernate, in 5 easy steps

I’ll use the example of the “Flashcard” object in the quizzing application which had a front, back, competition information, etc.

Step 1) Interfaces

For the Flashcard object first we built the interfaces that would define the main data parts of the object, both on the model side and on the Silverlight side.

   1:  namespace TBC.Interfaces
   2:  {
   3:      public interface IFlashcard
   4:      {
   5:          int PKID { get; set; }
   6:          int? TBCCompetition { get; set; }
   7:          int? TBCYear { get; set; }
   8:          int? KBCCompetition { get; set; }
   9:          int? KBCYear { get; set; }
  10:          string Front { get; set; }
  11:          string Back { get; set; }
  12:          int? DeckID { get; set; }
  13:          IQuestionType QuizQuestionType { get; set; }
  14:      }
  15:  }

A pretty simple object by most standards – a primary key, competition information, strings for the front of the card and back of the card, a “deck id” to allow for multiple flashcard decks to be prepared, and an IQuestionType.  This is the only custom type and interface in this object. The question type is a complex object that is basically like “multiple choice question”, “fill in the blank question”, etc.

Step 2) Build the Objects

Nothing fancy here either – just implement the interface into an object, but with a little twist.

   1:  using System.Runtime.Serialization;
   2:  using TBC.Interfaces;
   3:   
   4:  namespace TBC.Models
   5:  {
   6:      [DataContract]
   7:      public class FlashcardEntity : IFlashcard
   8:      {
   9:          [DataMember]
  10:          public QuestionTypeEntity QuizQuestionType { get; set; }
  11:   
  12:          #region IFlashcard Members
  13:          [DataMember]
  14:          public int PKID { get; set; }
  15:          [DataMember]
  16:          public int? TBCCompetition { get; set; }
  17:          [DataMember]
  18:          public int? TBCYear { get; set; }
  19:          [DataMember]
  20:          public int? KBCCompetition { get; set; }
  21:          [DataMember]
  22:          public int? KBCYear { get; set; }
  23:          [DataMember]
  24:          public string Front { get; set; }
  25:          [DataMember]
  26:          public string Back { get; set; }
  27:          [DataMember]
  28:          public int? DeckID { get; set; }
  29:   
  30:          IQuestionType IFlashcard.QuizQuestionType
  31:          {
  32:              get { return QuizQuestionType; }
  33:              set { QuizQuestionType = (QuestionTypeEntity) value; }
  34:          }
  35:   
  36:          #endregion
  37:      }
  38:  }

 

Nothing different here, except maybe the [DataContract] and [DataMember] tags. These are added via the System.Runtime.Serialization namespace and will be used to enable the WCF services to expose these objects to Silverlight.

So now we have an object with an interface that is ready to be served up by our WCF service.  Now all we need is to get it in and out of the database.  This leads us to NHibernate 2.0 and the Fluent NHibernate library, whcih leads to …

Step 3) The NHibernate Mapping File

Here is the basic mapping file for this object – and yes it is just another class.  It is important to recognize that it is completely separate from the object itself.

   1:  using FluentNHibernate.Mapping;
   2:   
   3:  namespace TBC.Models.Flashcard
   4:  {
   5:      public class FlashcardMap : ClassMap<FlashcardEntity>
   6:      {
   7:          public FlashcardMap()
   8:          {
   9:              TableName = "quiz_flashcards";
  10:              Id(f => f.PKID);
  11:              Map(f => f.TBCCompetition);
  12:              Map(f => f.TBCYear);
  13:              Map(f => f.KBCCompetition);
  14:              Map(f => f.KBCYear);
  15:              Map(f => f.Front);
  16:              Map(f => f.Back);
  17:              Map(f => f.DeckID);
  18:              References(f => f.QuizQuestionType);
  19:          }
  20:      }

 

This is a little confusing at first, but if you go through it step-by-step then you can understand what it does, even if not how it is actually implemented.  It is a brand new class inheriting from ClassMap<T> and we pass in the FlashcardEntity as the generic type.  Then, in the constructor, we simply define which table in the database contains the Flashcard data, and which element is the primary key.  Since we named the columns in the database the same as the property names we don’t need to use the overload with the column names.  Because of that all we need to do is to add all the Mappings with a simple lambda expression.  The References call is cool.  It “says” that “there is a column called QuizQuestionTypeID that references a single QuizQuestionType object, please go get it for me.”

Step 4) Initialize the Connection and Mapping.

So how do we initialize the connection and mappings we’ve defined?  Once they are all entered and mapped we do this …

   1:      IPersistenceConfigurer persistenceConfigurer =
   2:          MsSqlConfiguration
   3:              .MsSql2000
   4:              .ConnectionString.Is(MainController.GetInstance().Settings.ConnectionString);
   5:   
   6:      _cfg = persistenceConfigurer.ConfigureProperties(new Configuration());
   7:   
   8:      var persistenceModel = new PersistenceModel();
   9:      persistenceModel.Conventions.GetForeignKeyName = (prop => prop.Name + "ID");
  10:      persistenceModel.Conventions.GetForeignKeyNameOfParent = (prop => prop.Name + "ID");
  11:      persistenceModel.addMappingsFromAssembly(Assembly.Load("TBC.Models"));
  12:      persistenceModel.Configure(_cfg);

These are more Fluent NHibernate calls that handle implementing the configuration of NHibernate.  Yes this can all be done with configuration files, but now it can be done in code as well – fairly easily.

Step 5) The basic CRUD code

So now that this is all done, what would the code look like to get all the Flashcards from the database …

   1:  public List<T> GetAll<T>()
   2:  {
   3:        ISession session = NHSessionHelper.GetInstance().GetNewSession();
   4:        List<T> e = default(List<T>);
   5:        e = new List<T>(session.CreateCriteria(typeof (T)).List<T>());
   6:        session.Close();
   7:        return e;
   8:  }

or maybe just get one of the cards by ID and Save/Update …

   1:  public T GetByID<T>(object id)
   2:  {
   3:      ISession session = NHSessionHelper.GetInstance().GetNewSession();
   4:      T e = default(T);
   5:      e = session.Load<T>(id);
   6:      session.Close();
   7:      return e;
   8:  }
   9:   
  10:  public T SaveOrUpdate<T>(T saveEntity)
  11:  {
  12:      ITransaction transaction;
  13:      ISession session = NHSessionHelper.GetInstance().GetNewSession(out transaction);
  14:      session.SaveOrUpdate(saveEntity);
  15:      transaction.Commit();
  16:      session.Close();
  17:      return saveEntity;
  18:  }

Even with the error handling removed for brevity, this is pretty simple code.  In fact, you will notice that there is no mention in any of these methods of the FlashcardEntity classes.  This is because these generic methods can be used for any object that is created and mapped in this way.

Looking back at this there is really only 30 lines of code for the objects (interface, object, and mapping) and the rest of this code is reusable for all objects in the solution.  While these 30 lines could be easily generated, it is a nice number of lines of code to continue crafting code “by hand” and knowing as much as possible about what is going on in your objects.

It is also important to keep your objects “thin” when doing a lot of serialization and deserialization – more on that in post #3.