27

I'm considering one of two IRepository interfaces, one that is a descendant of IQueryable and one that contains IQueryable.

Like this:

public interface IRepository<T> : IQueryable<T>
{
    T Save(T entity);
    void Delete(T entity);
}

Or this:

public interface IRepository<T>
{
    T Save(T entity);
    void Delete(T entity);
    IQueryable<T> Query();
}

LINQ usage would be:

from dos
in ServiceLocator.Current.GetInstance<IRepository<DomainObject>>()
where dos.Id == id
select dos

Or...

from dos
in ServiceLocator.Current.GetInstance<IRepository<DomainObject>>().Query
where dos.Id == id
select dos

I kinda like the first one, but it's problematic to mock. How have other people implemented LINQable, mockable repositories?

bdukes
  • 152,002
  • 23
  • 148
  • 175
Andy S
  • 8,641
  • 6
  • 36
  • 40
  • Just to confirm? If you return **all** entities in `Query()` and then use linq in your consuming code to do a search, it would lazy load right? It wouldn't actually load thousands of records and then search them in your consuming code? – gideon Dec 22 '10 at 17:52

3 Answers3

14

Depends on if you want a Has-A or an Is-A relationship.

The first one is an Is-A relationship. The IRepository interface is a IQueryable interface. The second is a has-a. The IRepository has an IQueryable interface. In the process of writing this, I actually like the second better then the first, simply because when use your second IRepository, I can give the Query() method ANYTHING that returns IQueryable. To me, that is more flexible then the first implementation.

MagicKat
  • 9,695
  • 6
  • 32
  • 43
  • This is what I ended up doing.. Mocking Example 1 is just too painful, with Moq anyway. – Andy S Oct 03 '08 at 20:58
  • Related question, I'm using Moq to mock a repository like Example 2, but my query contains mapping logic in the "select new { ... }" that never gets tested because execution is deferred until IRepository.Save(), which is mocked and never performs the mapping. Any ideas? – flipdoubt Jan 19 '09 at 12:47
9

Personally, I use the Repository Pattern to return all items from the Repository as an IQueryable. By doing this, my repository layer is now very very light, small .. with the service layer (which consumes the Repository layer) can now be open to all types of query manipulation.

Basically, all my logic now sits in the service layer (which has no idea what type of repository it will be using .. and doesn't want to know <-- separation of concerns) .. while my repository layer is just dealing with Getting data and Saving data to the repo (a sql server, a file, a satellite in space.. etc <-- more separation of concerns).

eg. More or less pseduo code as i'm remembering what we've done in our code and simplifying it for this answer...

public interface IRepository<T>
{
    IQueryable<T> Find();
    void Save(T entity);
    void Delete(T entity);
}

and to have a user repository...

public class UserRepository : IRepository<User>
{
    public IQueryable<User> Find()
    {
        // Context is some Entity Framework context or 
        // Linq-to-Sql or NHib or an Xml file, etc...
        // I didn't bother adding this, to this example code.
        return context.Users().AsQueryable();
    }

    // ... etc
}

and now for the best bit :)

public void UserServices : IUserServices
{
    private readonly IRepository<User> _userRepository;

    public UserServices(IRepository<User> userRepository)
    {
        _userRepository = userRepository;
    }

    public User FindById(int userId)
    {
        return _userRepository.Find()
            .WithUserId(userId)
            .SingleOrDefault();  // <-- This will be null, if the 
                                 //     user doesn't exist
                                 //     in the repository.
    }

    // Note: some people might not want the FindBySingle method because this
    //       uber method can do that, also. But i wanted to show u the power
    //       of having the Repository return an IQuerable.
    public User FindSingle(Expression<Func<User, bool>> predicate)
    {
        return _userRepository
            .Find()
            .SingleOrDefault(predicate);
    }
}

Bonus Points: WTF is WithUserId(userId) in the FindById method? That's a Pipe and Filter. Use them :) love them :) hug them :) They make your code SOOO much readable :) Now, if u're wanting to know what that does.. this is the extension method.

public static User WithId(this IQueryable<User> source, int userId)
{
    return source.Where(u => u.UserId == userId).SingleOrDefault();
}

HTH's even though this question is .. well ... nearly two years old :)

Pure.Krome
  • 84,693
  • 113
  • 396
  • 647
0

You could always quick write stuff against List, it's not mocking using a mock framework, but it sure works great.

Kim Johansson
  • 372
  • 2
  • 8