Login or Sign Up to become a member!

EXPERTS, INFORMATION, IDEAS & KNOWLEDGE

Social bookmarker Add this

Your profile

Search

January 2009
Mon Tue Wed Thu Fri Sat Sun
 << <   > >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

XML Feeds

Authors

« StructureMap: Configure everything before using Objectfactory.GetInstanceSome of the blogs I read »
The Desktop Developers Journal

My Path to the Dark Side part 4 - the Repositories

by AlexCuse


Permalink 28 Jul 2008 07:07 , Categories: Microsoft Technologies, C# Tags: c#, nhibernate, unit testing

Previous posts can be found here:

Setting up the repositories for our objects is where this really starts to get fun for me. This is what allows us to work with the persisted objects so easily from our application code, without all the SQL getting in the way. The first thing we want to think about here is what we need the repository to do. Add/Delete/Update all come to mind of course. As well as retrieval of single objects and collections. These will be pretty much standard behaviors across most of our objects. So lets’ look at the interface first:

  1. using System;
  2. using System.Collections.Generic;
  3. using RecipeTracker.Model;
  4.  
  5. namespace RecipeTracker.Interfaces
  6. {
  7.     public interface IRecipeRepository
  8.     {
  9.         void Add(Recipe recipe);
  10.         void Update(Recipe recipe);
  11.         void Remove(Recipe recipe);
  12.         Recipe GetByID(int id);
  13.         ICollection<Recipe> GetByFamily(string family);
  14.         ICollection<Recipe> GetAll();
  15.         void Dispose();
  16.     }
  17. }

Looking at all those methods, there is really only one (GetByFamily) that we won’t need for any repository that we create. So we can put all the other methods into a BaseRepository class. We will need use generics so we can return all the different types however. But first, we need to get a session, so we can add a class for that.

  1. using System;
  2. using NHibernate;
  3. using NHibernate.Cfg;
  4.  
  5. namespace RecipeTracker.Repositories
  6. {
  7.     public class SessionProvider<T> where T:new()
  8.     {
  9.         private static ISessionFactory _sessionFactory;
  10.  
  11.         private static ISessionFactory SessionFactory
  12.         {
  13.             get
  14.             {
  15.                 if (_sessionFactory == null)
  16.                 {
  17.                     var configuration = new Configuration();
  18.                     configuration.Configure();
  19.                     configuration.AddAssembly(typeof(T).Assembly);
  20.                     _sessionFactory = configuration.BuildSessionFactory();
  21.                 }
  22.  
  23.                 return _sessionFactory;
  24.             }
  25.         }
  26.  
  27.         public static ISession OpenSession()
  28.         {
  29.             return SessionFactory.OpenSession();
  30.         }
  31.     }
  32. }

The SessionFactory part should look familar from part 3, the only difference here is that we are initializing the configuration using TypeOf(T) to determine which assembly to find the configuration in rather than TypeOf(MyType). We could probably get away with the latter for this purpose, because there probably won’t be more than one assembly in the application, but why be lazy right? After all, we do need to use generics to deal with the return types anyways.

So now this little bit of code doesn’t need to be handled by our repository, and it can focus on what it does best. So here’s the BaseRepository, it’s nice and simple since it doesn’t need to get its’ own sessions anymore:

  1. using System;
  2. using System.Collections.Generic;
  3. using NHibernate;
  4. using NHibernate.Cfg;
  5.  
  6. namespace RecipeTracker.Repositories
  7. {
  8.     public abstract class BaseRepository<T> where T: new()
  9.     {
  10.         protected ISession _session = SessionProvider<T>.OpenSession();
  11.  
  12.         public T GetByID(int id)
  13.         {
  14.             return _session.Get<T>(id);
  15.         }
  16.  
  17.         public ICollection<T> GetAll()
  18.         {
  19.             var products = _session
  20.                 .CreateCriteria(typeof(T))
  21.                 .List<T>();
  22.             return products;
  23.         }
  24.  
  25.         public void Update(T toUpdate)
  26.         {
  27.             using (ITransaction transaction = _session.BeginTransaction())
  28.             {
  29.                 _session.Update(toUpdate);
  30.                 transaction.Commit();
  31.             }
  32.         }
  33.  
  34.         public void Add(T toAdd)
  35.         {
  36.             using (ITransaction transaction = _session.BeginTransaction())
  37.             {
  38.                 _session.Save(toAdd);
  39.                 transaction.Commit();
  40.             }
  41.         }
  42.  
  43.         public void Remove(T toRemove)
  44.         {
  45.             using (ITransaction transaction = _session.BeginTransaction())
  46.             {
  47.                 _session.Delete(toRemove);
  48.                 transaction.Commit();
  49.             }
  50.         }
  51.  
  52.         public void Dispose()
  53.         {
  54.             _session.Close();
  55.             _session.Dispose();
  56.         }
  57.     }
  58. }

Now, look how simple that is to do what we need with our object? No building SQL queries, no creating parameter arrays, or anything. A nice simple bit of code that does just what we need it to do, without all the hassles. We just need a session and the simple commands that it offers, and we can do anything we need. Beautiful, right?

But what if we wanted to do something like get all recipes from a certain family? This will be specific to the object type we need, so we can do that in our implementation of the baseclass (hey, gotta have something in there right!). And this is in fact all that our RecipeRepository class has, one method:

  1. using System;
  2. using System.Collections.Generic;
  3. using RecipeTracker.Model;
  4. using NHibernate;
  5.  
  6. namespace RecipeTracker.Repositories
  7. {
  8.     public class RecipeRepository : BaseRepository<Recipe>, Interfaces.IRecipeRepository, IDisposable
  9.     {
  10.         public ICollection<Recipe> GetByFamily(string family)
  11.         {
  12.                 var products = _session
  13.                     .CreateCriteria(typeof(Recipe))
  14.                     .Add(NHibernate.Criterion.Expression.Eq("Family", family))
  15.                     .List<Recipe>();
  16.                 return products;
  17.         }
  18.     }
  19. }

This is some wacky looking code at first. It kind of reminds me of Linq, but what its’ called is HQL (Hibernate Query Language). I haven’t really gotten into it all that much, so I don’t feel qualified to speak about it in detail, but I do find it kinda cool. It may not be as easy as Linq, where you could do a nice Linq query such as

recipeList.Where(n => n.Family = family)

But remember this needs to work on older framework versions as well. I think the HQL is reasonably succinct, and even somewhat elegant. After all reading that you can just about instantly tell what it does. It just creates a criteria on the session for Recipes, and then defines the expression to be evaluated (table.Family = family). Pretty cool. We could probably figure out how to do this with generics pretty easily, but I figure this kind of method is going to be tied to your specific type, and you want to have a descriptive name, and all that.

So after all this I think we are ready to set up some tests. This will be on the next page (damn I’m getting long-winded!)

Pages: 1 · 2

2 comments »Send a trackback » 486 views

Trackback address for this post

Trackback URL (right click and copy shortcut/link location)

2 comments

Comment from: Jamie [Visitor]
****-
Why is BaseRepository abstract? Many of your business objects won't need any additional methods. And why doesn't BaseRepository implement IDisposable instead of implementing in each concrete class as you're doing?

I realize it's textbook stuff, but IRecipeRepository doesn't add any value here.

I'm enjoying the series, good stuff.
07/28/08 @ 08:49
Comment from: AlexCuse [Member] Email
Jamie - good point, It could be helpful to be able to instantiate BaseRepository, although in this case I belive I had some kind of "special" behavior in each implementation. I'll have to take a look at the project next time I get the chance.

BaseRepository didn't implement IDisposable because of an oversight on my part.

Both these problems come from the fact that I'd already written all the Repositories as concrete classes before I realized I could even create BaseRepository, so I was kind of working backwards from that point and overlooked a few things. Helped me get a bit more familiar with nhibernate, but at what cost? ;(

Finally, I know IRecipeRepository doesn't add any value, but I'm hopelessly addicted to interfaces. Just knowing that someday, they might add some value, is enough for me ;)

Glad you've been enjoying the articles.
07/28/08 @ 11:20

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.
PoorExcellent
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)