3

I'd like to expose a Repository as an 'IQueryable' type.

The repository uses Linq to NHibernate to communicate with the database.

Can anyone point me at an example implementation?

For example, what would the corresponding 'GetEnumerator()' implementation on my repository look like?

Edit:

Would something like this be appropriate?

public class MyTypeRepository : IEnumerable<MyType>
{        
    IEnumerator<MyType> IEnumerable<MyType>.GetEnumerator()
    {
        return Session.Linq<MyType>().GetEnumerator();
    }


    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable<MyType>)this).GetEnumerator();
    }

}
Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • IQueryable implementes IEnumerable I believe (which would include GetEnumerator()), so not sure what you are asking? What reference for the Repository pattern are you using? – Lazarus Jul 14 '10 at 13:48
  • Having done some research, perhaps I mean expose as IEnumerable. I'd like Linq queries sent to the repository passed straight through to Linq to NHibernate. ...so I'd like to be able to write _myRepo.Single(x => x.Id = blah); – Ben Aston Jul 14 '10 at 13:54
  • Question: Wouldn't returning an enumerable then adding a where clause pull the entire data set then filter out records locally rather than filtering records before leaving the data source? – Zachary Scott Aug 11 '11 at 18:27

5 Answers5

5

This is a bad design.

An IQueryable is a question (lookup "query" in a dictionary). It's how you ask for data. It's what you should be giving to the Repository.

A Repository should be returning answers -- the data itself.

If the Repository is returning a IQueryable, you've pretty much negated the need for the Repository.

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • I don't agree with this assessment. If your repository exposes IQueryable objects, you can tell it how to query in an abstract way. That's how Linq to SQL works and I build repositories on top of that all the time. – BC. Jul 14 '10 at 16:15
  • 1
    @BN: Your timeline is confused. If your Repos returns an IQueryable, how can you use that to tell the Repos anything? You've gotten your object from the Repository. You are done with it. Perhaps you meant this as a conversation: You get an IQ (from somewhere, maybe the Repos), you modify it, you give it back to the Repos, you get data. This cooresponds more to my version than yours. – James Curran Jul 14 '10 at 16:40
  • -1 An IQueryable object is something [that can be queried](http://en.wiktionary.org/wiki/queryable). It's not a query itself. By calling LINQ methods on the IQueryable, you're building an [Expression](http://msdn.microsoft.com/en-us/library/system.linq.expressions.expression.aspx) object. This Expression represents the query. – Niels van der Rest Jul 24 '10 at 08:45
  • 1
    @Niels: Your dealing too much with implementation details here, and anyway, again we go back to the timeline. A query (in whatever form) goes TO the Repository and data come OUT. That's the important issue here. – James Curran Jul 24 '10 at 10:55
  • 4
    The data is *exposed* as an IQueryable, e.g. Entity Framework exposes database tables as IQueryables. Look at the statement `customers.Where(c => c.City == "London").Take(10).Average(c => c.Age)`. The `customers` variable is the full dataset. The `Where` statement filters the original data and results in a **new dataset**. This dataset is narrowed down further by the `Take` and `Average` statements. The fact that the entire statement *may be* converted to a single optimized SQL query, *that's* an implementation detail. But if you don't agree, we'll have to disagree. – Niels van der Rest Jul 24 '10 at 11:56
  • I would think this is where the specification objects come in to play. – Zachary Scott Aug 11 '11 at 18:23
  • 1
    +1 If the repository returns IQueryables it negates the need for the Repository. EF itself is an implementation of the Repository and Unit of Work pattern. http://programmers.stackexchange.com/questions/162438/returning-an-iqueryable-from-an-irepository – Jerry Joseph May 19 '13 at 12:59
4

I think a Repository can give you 1 or more IQueryables/IEnumerables, but not : a Repository is an IQueryable.

It could look like:

 public interface IPersonRepository
 {
    IEnumerable<Person> GetAllPersons();
    void AddPerson(Person person);

    // more...
 }

You could return IQueryable<Person> from GetAllPerson() but that may not be an improvement. IEnumerable is simpler, less coupling.

David Gardiner
  • 16,892
  • 20
  • 80
  • 117
H H
  • 263,252
  • 30
  • 330
  • 514
2

Lots of opinions of this one, but Ayende (Oren Eini) seems to think IQueryable is ok to return and articulates his point rather well.

Josh
  • 44,706
  • 7
  • 102
  • 124
  • And he doesn't have a Repository in that example. He has essencally eliminated his DAL. Which is fine, just don't call the vacuum left a DAL. – James Curran Jul 14 '10 at 17:04
1

Just return session.Linq<T>()

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • If you do this make sure you're careful with where the Queryable gets passed around to so you don't accidentally create full table scans. – Chris Marisic Jul 14 '10 at 14:00
1

I can see two possible solutions here:

  1. Expose IEnumerable and use .AsQueryable() when needed

    // Repository
    public IEnumerable<Person> GetAll() {
        return _dbContext.People.AsQueryable();
    }
    
    // Usage
    public Person GetByPhone(string phoneNumber) {
        var queryablePeople = _personRepository.GetAll().AsQueryable();
        return queryablePeople.FirstOrDefault(perspn => person.Phone == phoneNumber);
    }
    
  2. Accept expression in Repository method

    // Repository
    public IEnumerable<Person> GetPeople(Expression<Func<Person, bool>> filter) {
        return _dbContext.People.Where(filter);
    }
    
    // Usage
    public Person GetByPhone(string phoneNumber) {
        return _personRepository.GetPeople(person => person.Phone == phoneNumber).FirstOrDefault();
    }
    

Note: Not any expression filter can be translated to SQL Query by Entity Framework

Vasyl Chuy
  • 31
  • 3