Repositories 101

I got this question in an email today, and I think that it would make a good post. I broke it into two parts:

We are moving from Delphi (RAD way) to  real OO in C# + NHibernate.  I've reading about DDD. Now I'm kind of lost about Repositories + NHibernate. I found code that uses a Repository<T> class, which I liked at first, but after a while I wondered... how can i test it ? or, tomorrow we can change from nhibernate to other thing...

Let us talk about my Repository<T>, it is a static class, so it can't be mocked, but it is fully testable, since it uses Windsor to get the implementation of the IRepository<T> interface, of which there are several. Now consider the following code:

public ICollection<Customer> GetPreferredCustomers()
{
  return Repository<Customer>.FindAll(Where.Customer.IsPreferred = true);
}

Is it testable? Yes, I can replace the internal implementation of the Repository<T> with my own, in this case, a mock. The test would look something like:

[Test]
public void GetPreferredCustomerWillForwardQueryToRepository()
{
   //already setup with a fresh mock that it will return
   IRepository<Customer> customersRepository = IoC.Resolve<IRepository<Customer>>();
   Expect.Call(customersRepository.FindAll(null,null))
     .Constraints(Criteria.Contains("IsPreferred = True"), Is.Anything). Return(null);
   mocks.ReplayAll();
   service.GetPreferredCustomers();
   mocks.VerifyAll();
}

As you can see, it is not hard, but neither is it trivial. And I am not testing that the query works. For this types of situations, I am using in memory database and test against known data. I feel that it gives me as much value, and it doesn't hinder the tests.

How can I build a nice repository that could allow  change its "core", well, from nhibernate to what else.. I thought about an IRepository<T> interface, and than have a NHRepository : IRepository<T>, and in my code I referece to IRepository, and not to NHRepository...   am in in the right way ?

If you really like, you can do it by taking an interface like my IRepository<T> and use that, just stripping all the NHibernate specific parts. The reason I don't like it is that it is a false promise. You won't be able to easily switch to another ORM easily. Each ORM does it magic differently, and trying to move from NHibernate to LLBLGen (or vice versa), to make a simple example, is not going to be "let us just change the repository implementation" and that is it.

That would actually be the easy part, the far harder issue would be to make sure that logic that relies on the existance of Unit of Work (or auto wiring of reverse dependencies, to take something that NHibernate does not do), is much harder. You can limit yourself to simple CRUD operations, in which cases the differences are neglible in most scenarios, but that is missing out a lot, in my opinion.

Print | posted on Tuesday, March 20, 2007 2:08 AM

Feedback


Gravatar

# re: Repositories 101 3/20/2007 4:27 AM Christopher Bennage

These sort of posts are really useful. Thanks.


Gravatar

# re: Repositories 101 3/20/2007 5:01 AM Steve

I agree, Please continue Ayende :)

Thank you sir!


Gravatar

# re: Repositories 101 3/20/2007 11:40 AM Andrew Peters

Having a truly generic repository becomes a bit easier with LINQ. I implemented a version of your Repository<T> pattern on a recent project that used LINQ to SQL and was able to substitute an in-memory database no problems. Repository finders accept LINQ expressions that can be used to query either the database or in-memory collections.



Gravatar

# re: Repositories 101 3/20/2007 11:53 AM Ayende Rahien

That may be a great solution to this, yes.
I am concerned about the difference in implementation, but that puts a general query in the hand of the repository, and the burden of making it work is with the hands of the repository, not the app developer, which is as it should be.


Gravatar

# re: Repositories 101 3/20/2007 12:39 PM Paulo Quicoli

thanks for make things clear !


Gravatar

# re: Repositories 101 5/9/2007 12:12 AM Varela

Can you please sketch the ideea how to setup mocks for sample that you provide?
Sorry, i'm new to Rhino & Windsor, but i find these tools just great. I stuck some time with [SetUp] section of my test fixture, but still not able to wire together MockRepository, Repository<T>, WindsorContainer & IoC .
Thanks in advance.
And thank you for your posts. You provide a really good content. You do a really good job.


Gravatar

# re: Repositories 101 5/9/2007 12:20 AM Ayende Rahien

The code above makes a few assumptions, that you are using Windsor, that you are using Rhino Common's repository, etc.
Basically, the code in the setup is something like:

mockedContainer = mocks.DynamicMock<IWindsorContainer>();
customersRepository = mocks.DynamicMock<IRepository<Customer>>();
SetupResult.For(mockedContainer.Resolve<IRepository<Customer>>())
.Return( customersRepository ) ;


Gravatar

# re: Repositories 101 5/9/2007 10:34 AM Varela

Ok, but as i understand, the next step should be initializing the IoC class with instance of mockedContainer:
IoC.Initialize(mockedContainer);
And after that, in our tests we should be able to retrieve from IoC our mocked instance of customerRepository:
IRepository<Customer> customersRepo = IoC.Resolve<IRepository<Customer>>();
But this step fails for me - instance is always null. I'm doing something wrong? Or i just have entered in "stupid mode" and i'm not seeing evident things? :)
Anyway, thank you for the responce...


Gravatar

# re: Repositories 101 5/9/2007 10:34 AM Varela

Ok, but as i understand, the next step should be initializing the IoC class with instance of mockedContainer:
IoC.Initialize(mockedContainer);
And after that, in our tests we should be able to retrieve from IoC our mocked instance of customerRepository:
IRepository<Customer> customersRepo = IoC.Resolve<IRepository<Customer>>();
But this step fails for me - instance is always null. I'm doing something wrong? Or i just have entered in "stupid mode" and i'm not seeing evident things? :)
Anyway, thank you for the responce...


Gravatar

# re: Repositories 101 5/9/2007 1:51 PM Ayende Rahien

You haven't moved the mocked instance to replay mode, probably.

Comments have been closed on this topic.