Should I be specifying IQueryable over List in my interfaces? - c#

I'd like to know whether, when defining my interface, I should prefer IQueryable over List for groups of objects. Or perhaps IEnumerable is better, since IEnumerable types can be cast to IQueryable to be used with LINQ.
I was looking at a course online and it was dealing with EntityFramework, for which it is best to use IQueryable since LINQ uses it (okay there's more to it than that but probably not important right now).
The code below was used, and it got me thinking if I should be specifying IQueryable instead of List for my groups of objects.
namespace eManager.Domain
{
public interface IDepartmentDataSource
{
IQueryable<Employee> Employees { get; }
IQueryable<Department> Departments { get; }
}
If I were building an interface for a service that calls the repository to get Employees, I would usually specify
List <Employees>
but is this best practice? Would IQueryable give more flexibility to classes that implement my interface? What about the overhead of having to import LINQ if they don't need it (say they only want a List)? Should I use IEnumerable over both of these?

The IQueryable interface is in effect a query on Entity Framework that is not yet executed. When you call ToList(), First() or FirstOrDefault() etc. on IQueryable Entity Framework will construct the SQL query and query the database.
IEnumerable on the other hand is 'just' an enumerator on a collection. You can use it to filter the collection but you'd use LINQ to Objects. Entity Framework doesn't come into play here.
To answer your question: it depends. If you want the clients of your repository to be able to further customize the queries they can execute you should expose IQueryable, but if you want full control in your repository on how the database is queried you could use IEnumerable.
I prefer to use IEnumerable, because that doesn't leak the use of Entity Framework throughout the application. The repository is responsible for database access. It is also easier to make LINQ to EF optimizations, because the queries are only in the repository.

What I usually do is make the repositories return IQueryable. Then in the BL I specify either IEnumerable or IQueryble. It is important to know the main differences between IQueryble and IEnumerable.
Lets say you fetch data into IEnumerable
IEnumerable employees=this.repository.GetAll();
Now let's say this specific function require only employees with age over 21 and the others are not needed at all.
You would do:
employees.Where(a=>a.Age>21)
In this case the original query will be loaded in the memory and then the Where will be applied.
Now lets say you change the function to fetch the data into IQueryable
IQueryable employees=this.repository.GetAll();
employees.Where(a=>a.Age>21)
This time when you modify the query with the Where clause, the whole query will be executed in the database (if possible) and you will only get employees with age over 21 from the database.
In the IEnumerable case you will get all the employees from the database and then they will get filtered in memory to satisfy the where condition.
Use IEnumerable, IList or something else?
If you know what operations will be executed on the collection you can easily choose which interface to use. Basically if you will only iterate over the collection you would use IEnumerable. If you would do more operations you need to choose the appropriate interface. There are good videos of .NET collections in pluralsight.

Related

Whats the difference between IQueryable and DbQuery?

Following up on this question/answer
How to make Entity Framework Data Context Readonly
The solution is to make your DbContext collections of the type DbQuery, but that is a rather specialized type (it's buried down in the namespaces for EF).
So, what's the functional difference between have a DbContext with this:
public DbQuery<Customer> Customers
{
get { return Set<Customer>().AsNoTracking(); }
}
vs this:
public IQueryable<Customer> Customers
{
get { return Set<Customer>().AsNoTracking(); }
}
...the EF documentation is very light when it comes to the DbQuery class, but I prefer the idea of having DbContext made up of interfaces rather than classes so I'd like to avoid it. What additional benefits does the DbQuery class provide?
Update
After reading the answers and just looking at the code I realized my question was a little silly. I was too quick to ask before I thought! Obviously the underlying concrete object will be a DbQuery regardless, so the actually inner functionality will be the same. It seems to me that using IQueryable is the better choice. Thanks for your patience!
DBQuery is a non-generic LINQ to Entities query against a DbContext. Exposing this will give you LINQ functionality against Entities. If you don't need this, use the IQueryable interface abstraction.
IOrderedQueryable
Intended for implementation by query providers.
This interface represents the result of a sorting query that calls the method(s) OrderBy, OrderByDescending, ThenBy or ThenByDescending. When CreateQuery is called and passed an expression tree that represents a sorting query, the resulting IQueryable object must be of a type that implements IOrderedQueryable.
IListSource
Provides functionality to an object to return a list that can be bound to a data source.
IDbAsyncEnumerable
Asynchronous version of the IEnumerable interface that allows elements to be retrieved asynchronously. This interface is used to interact with Entity Framework queries and shouldn't be implemented by custom classes.
This is an old question, but it comes up in a google search on DbQuery, so just to update things a bit:
In EF Core 2.1, QueryTypes are now mapped to DbQuery types, as described in the documentation located at
https://learn.microsoft.com/en-us/ef/core/modeling/query-types
Here are the relevant bits:
Query types have many similarities with entity types...
...
However they are different from entity types in that they:
Do not require a key to be defined.
Are never tracked for changes on the DbContext and therefore are never inserted, updated or deleted on the database.
Are never discovered by convention.
Only support a subset of navigation mapping capabilities - Specifically:
They may never act as the principal end of a relationship.
They can only contain reference navigation properties pointing to entities.
Entities cannot contain navigation properties to query types.
Are addressed on the ModelBuilder using the Query method rather than the Entity method.
Are mapped on the DbContext through properties of type DbQuery rather than DbSet
Are mapped to database objects using the ToView method, rather than ToTable.
May be mapped to a defining query - A defining query is a secondary query declared in the model that acts a data source for a query type.
You define a QueryType in your DbContext like a DbSet, but using the DbQuery type:
public virtual DbQuery<CustomClassThatMapsYourQueryResults> QueryResults { get; set; }
Then in your OnModelCreating method you indicate that your custom results object is mapped to a stored proc in the database:
modelBuilder.Query<CustomClassThatMapsYourQueryResults>();
Then in your data access code somewhere:
var someVariable = 1;
var someOtherVariable = "someValue";
...
var myResults = _dbContext.FromSql($"spStoredProcName {someVariable} '{someOtherVariable}'");
As far as I know, the only way to populate the DbQuery< T > object is to use the FromSql() method. The DbQuery< T > results of the FromSql() method return IQueryable< T >.

NHibernate collection Count() dilemma

I usually use a "Page -> Service -> Repository" pattern in my applications putting all my database calls in "repository" classes.
In some situations I've to test collection items count in my service logic.
Example:
EventService eventService=container.Resolve<EventService>();
IEnumerable<Event> events=eventService.GetAll();
if(events.Count()>0)
{
...do something...
}
Using NHibernate this will generate automatically a SQL statement with a DB access out of my repository class.
Can I avoid this? Is there a best practice?
I try to avoid exposing IQueryable<T> on repositories for the following reasons:
Many of the methods or not supported and it is not always clear what kind of query will be generated.
Supporting ad hoc queries undermines the importance of designing proper indexes for specific uses cases.
You lose the specification of the kind of queries that are expected.
Instead I would opt for using a simple query object which is translated into whatever it needs to be by the repository.
For this case in particular I would have a method on the repository returning a paginated list of events:
IList<Event> GetAll(int startIndex, int count);
This also highlights the importance of pagination. In the repository this would like something like:
this.Session.Linq<Event>().Skip(startIndex).Take(count).ToList();
You may also choose to create a wrapping object for IList which can contain the total count as well.
If you are (unit) testing your service layer then you should be mocking your repositories. You can create a mock repository with a static list of objects exposed as an IQueryable.
This way when you call Count() it will return a count of your static list, something that you can reliably assert.
Update
My mistake, I assumed from this line
I've to test collection items count in
my service logic
That you wanted to test this logic of your service.
Your above code looks like you are returning an IQueryable. So as soon as you call Count() or ToList() (essentially anything that enumerates the IQueryable) you will hit the database.
So either change your service to return IList<T> or change your code to the below:
EventService eventService=container.Resolve<EventService>();
IEnumerable<Event> events = eventService.GetAll().ToList();
if(events.Count()>0) { ...do something... }
You will hit the database when you call ToList(), but not when you call Count() as you are working with an in-memory collection.

LINQ navigation properties in the MVC controller action?

I'm kinda stuggling with this being best practice or not..
I have a repository that returns an IQueryable lets say. Is this correct usage in the controller?
var whatever = ObjectRepository.GetWhatever(id);
var videoId = whatever.UsersInObject1InObjects2.First().Object.Video.ExternalVideoId;
Where in the 2nd line above ".Object" and ".Video" are references to tables that are related to "whatever" table.
Or should I be making another function in a different repository to get the ExternalVideoId?
The way I usually do this is this. I create a separate model class that encapsulates data that the controller need from the database. (I.e. as a rule I do not use ORM class for this). This allows me annotating the model class members with all sort of attributes I might need. In the repository code I query the ORM and then return the model class (or IQueryable/IEnumerbale of model) as the result.
I've heard an opinion that returning IQueryable from an ORM such as Entity Framework is not advised, because by doing this, you risk executing a query against your back end several times if you are not careful with your model in controller/view. This is because in EF IQueryable represent a query that is not executed yet. By returning IEnumerable instead of IQueryable you make sure that query execution is confined to your repository class.
I however find, that sometimes it's more convenient to return IQueryable. For example, for table paging/sorting scenarios I can apply page number, sort direction, page size, etc to the IQueryable before executing it. I feel that this logic belongs more to controller rather then to repository. Some may disagree.
What you're actually doing is using a collection to do a query.
I think you need to have a better look at linq queries.
You don't want to add custom queries to your repository.
You just want to expose the queryable interface like so..
public IQueryable<T> Get( IUser identity )
{
return Context.Set<T>();
}
public IQueryable<IBindable> GetItem( IUser identity )
{
return Context.Set<T>().Cast<IBindable>();
}
then you can use linq to sql

NHibernate IQueryable collection as property of root

I have a root object that has a property that is a collection.
For example:
I have a Shelf object that has Books.
// Now
public class Shelf
{
public ICollection<Book> Books {get; set;}
}
// Want
public class Shelf
{
public IQueryable<Book> Books {get;set;}
}
What I want to accomplish is to return a collection that is IQueryable so that I can run paging and filtering off of the collection directly from the the parent.
var shelf = shelfRepository.Get(1);
var filtered = from book in shelf.Books
where book.Name == "The Great Gatsby"
select book;
I want to have that query executed specifically by NHibernate and not a get all to load a whole collection and then parse it in memory (which is what currently happens when I use ICollection).
The reasoning behind this is that my collection could be huge, tens of thousands of records, and a get all query could bash my database.
I would like to do this implicitly so that when NHibernate sees an IQueryable on my class it knows what to do.
I have looked at NHibernate's LINQ provider and currently I am making the decision to take large collections and split them into their own repository so that I can make explicit calls for filtering and paging.
LINQ To SQL offers something similar to what I'm talking about.
I've been trying to come up with a solution for a similar problem.
You can filter collections off an entity using ISession.FilterCollection. This creates an additional IQuery where you can count, page, add criteria, etc.
So, for example (my query in FilterCollection may be a little off, but you should get the idea):
ISession session = GetSession();
var shelf = session.Get<Shelf>(id);
var books = session.FilterCollection(shelf.Books, "where Name = :title").SetString("title", "The Great Gatsby").List<Book>();
There are a problem with that, however:
The consumer executing the code
needs to access
ISession.CreateFilter, or you need
to create a method on your
repository that takes in a property,
a query, and your query arguments
(as well as any paging or other
information). Not really the sexiest
thing on the planet.
It's not the LINQ you wanted.
Unfortunately, I don't think there's any way to get what you want out of the box with NHibernate. You could fake it, if you wanted to try, but they seem to fall flat to me:
Add a method or property that under the covers returns a LINQ to NHibernate IQueryable for this shelf:
public IQueryable<Book> FindBooks() {
return Resolver.Get<ISession>().Linq<Book>().Where(b => b.Shelf == this);
}
where someone might consume that like this:
var shelf = ShelfRepo.Get(id);
var books = (from book shelf.FindBooks()
where book.Title == "The Great Gatsby"
select book);
Yuck! You are bleeding your persistence needs through your domain model! Maybe you could make it a little less worse by having a repository emit IQueryable, which at runtime is actually LINQ to NHibernate:
public IQueryable<Book> FindBooks() {
return Resolver.Get<IRepository<Book>>().CreateQuery().Where(b => b.Shelf == this);
}
Still pretty blah.
Create your own custom collection type (and potentially an IQueryable implementation) that wraps a private field of the actual books, and map NHibernate to that field. However, it may be a difficult undertaking getting that working with ISession.CreateFilter. You have to consider "discovering" the current session, converting the LINQ expression into something you can use in CreateFilter, etc. Plus, your business logic is still dependent on NHibernate.
Nothing really satisfies at this point. Until NHibernate can do LINQ over a collection for you, it appears that you're better off just querying your Book repository normally as has already been suggested, even if it doesn't seem as sexy or optimal.
I tend to think like this:
Aggregate roots are boundaries of consistency, so if shelf needs to enforce some sort of consistency policies on the books it contains, then it should be an aggregate root.
And in such case it should hold a set/collection of books.
If you don't need to enforce consistency in any way from shelf to books, then I'd consider to remove the set/collection property and move those queries into a repository instead.
Also, since pagination and filtering most likely don't have anything to do with your domain logic, it is most likely for presentation.
Then I'd consider to make some special view for it instead of adding presentation facillities to my repositories.
e.g.
var result = Queries.FindBooksByShelf(shelfId,pageSize);
Such query could return projections and/or be optimized as plain SQL etc.
They are most likely specific for a certain view or report in your GUI.
This way your domain will focus on domain concepts only.
Perhaps you should give Nhibernate Linq a try. It allows you use the IQueryable and do things like:
Session.Linq<Book>().Where(b => b.Name == "The Great Gatsby");

Linq Working with DataContext

1) I wish to clarify some doubts on collections.
SampleDBDataContext PersonDB = new SampleDBDataContext("");
Table<Person> p=PersonDB.GetTable<Person>();
IEnumerable<Person> per = PersonDB.GetTable<Person>();
IQueryable<Person> qry = PersonDB.Persons.Select(c => c);
what are the differences between using Table<Person>,IEnumerable<Person>,IQueryable<Person>.Any specific need to choose the particular one?
2) For Adding records Add() method not appears in my IDE,(i.e) PersonDB.Persons.Add().
What is the problem here?
1.
IEnumerable<> is an interface that
applies to any collection whose
members can be enumerated or iterated
over.
IQueryable<> is a LINQ interface
that applies to any collection whose
members can be lazily queried.
(queried without materializing the
result set until its members are
accessed)
Table<> is a class that I've
not used before but "represents a
table for a particular type in the
underlying database."
Which one you choose depends on what your needs are, but IEnumerable<> is the most general, so I would use that in my type declarations if it's sufficient.
2.
To insert a person use InsertOnSubmit():
Person person = new Person() { ... };
PersonDB.Persons.InsertOnSubmit(person);
PersonDB.SubmitChanges();
Table(T) is the class that LINQ to SQL uses for query results from a table; it is also used for properties of the data context, so that you can use the data context properties in your queries. So the code in your post is duplicating some code that LINQ is already doing for you. PersonDB.Persons should always be sufficient for querying persons.
For results, you will likely want a collection, so while IEnumerable and IQueryable are fine, you can also consider using a list:
List<Persons> pers = PersonDB.Persons.Where(p => p.name == aName).ToList();
Note that this is an immediate execution, not a lazy (deferred) execution. ToList() forces immediate query execution.
Table<> is well, a table. It can return IQueryable results and can perform inserts/other database specific operations. It is linked to a DataContext.
IQueryable<> is a list that is well, not a list. Basically IQueryable doesn't actually load any data until you actually request it (late execution). As a result you can perform multiple operations before the whole thing is sent to the database. Think of IQueryable<> as an SQL statement in progress, rather then the actual results.
IEnumerable<> is just a standard interface for any list of objects that can be enumerated. When an IQueryable actually executes, you get an IEnumerable set to go through.
Add appears for indvidual instances - for example Person.Add(). For raw tables you should use the Insert* methods.

Categories