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 >.
Related
NB I'm not asking how to find/filter entities in the DB. I'm not asking about the difference between Find and Where. I'm not asking about the implication of generics.
According to the docs, the DbContext provides methods Find and Find<T>. Usually, I go through the DbSet but noticing that there's Add() on both the context directly and the DB set, I wanted to see what will happen if I try to filter directly on the context too, instead of the DB set.
I haven't found a single example of such call, though. A lot of information on going through the DB set and the differences mentioned in the disclaimer at the top. But I see no sample showing usage of Find() nor Find<T>() directly on the context.
Is it possible at all and if so, how? Intellisense is cryptic...
It does the same thing, but without using the DbSet. It works by specifying the type (entity) you want to fetch. You just have to specify the type, which must match your DbSet.
Is it possible at all and if so, how?
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options) { }
public DbSet<City> Cities{ get; set; }
public DbSet<State> States { get; set; }
}
And use it like so:
MyContext context = CreateContext() // Or inject it...
// Will return the City which have id == 5.
object myUntypedCity = context.Find(typeof(City), 5);
// Will return the city which have id == [your guid].
City myTypedCity = context.Find<City>(Guid.NewGuid());
As a sidenote; Add() on the Context works the same way. It will detect which type of entity it is and put it on the correct DbSet. I do not know what will happen if you have several DbSet using the same type.
why we have a non-generic Find() on the context given that quite a few
DBs are using integers for IDs, hence ensuring ambiguous ID values
between different entities/tables
In some scenarios you might not expose the DbSet or you only know the type of the entity. This can be very handy when making some very generic code where you don't have a typed entity nor know exactly what type of id (primary key) it has. It may be a Guid or an int. I myself have never found a practical use for Find on the DbContext.
When you use Find on a DbSet, the Type of entity is known, so it just needs to Find by the provided ID.
When you use Find on the DbContext, you either have to tell EF which Type (Entity) to find against from its known mappings, or you can use the Generic version which defines the Type of entity to search against.
Why would you use these methods instead of DbSet.Find? One example would be where you don't want to expose an aspect of your domain as a DbSet. For example if you have a DbSet of Customers, Businesses, etc. that all contain references to Address entities it would be uncommon to ever need to load an Address outside of that scope so you would probably opt not to expose a DbSet<Address> in your domain to potentially be abused through convenience. In the case where your DbContext actually does need to locate a particular Address, you could use DbContext.Find<Address>(addressId) to load it.
Honestly I don't use DbContext.Find(), I don't even use DbSet.Find(), I much, much prefer to work with IQueryable and projection. But hey, it's an option. :)
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.
I have inherited a code base that uses DTOs in the business layer, these are populated via a set of mappers from Entity Framework. This has some quite serious limitation in terms of querying so I am working on a new "optimised" querying service.
My first issue is that I need to translate my LINQ query on my DTO to work with my Entity object but the calling context has no knowledge of the EF entities. Let's assume we can rely on the properties on each object having matching names.
This is where I have got to in terms of stubbing out what I want:
public static List<TDataObject> GetFiltered<TDataObject(Expression<Func<TDataObject, TDataObject>> projection, Func<TDataObject, bool> filter)
{
// 1. translate the filter parameter to work with my equivalent Entity object
// 2. build the EF query with the modified filter expression and also a Select() projection so we only return the properties we need. (this should generate an optimised SQL query under the hood)
// 3. map the results from the EF query back onto my TDataObject and return (I already have AutoMapper maps in place for this)
}
It is item 1 that I am struggling with so if anyone has any code examples for blogs posts for achieving this I'd appreciate it.
Also if anyone has any alternate suggestions I'd be happy to hear them.
One way to handle this is to build primitives around queries (instead of layers with repositories etc). Here's what we do:
http://lostechies.com/jimmybogard/2013/10/29/put-your-controllers-on-a-diet-gets-and-queries/
The calling code (controller) knows about a query and the result (DTO), but the piece doing the mapping knows exactly about EF Context/NHibernate ISession. Works very well for us and keeps our controllers light and thin.
Alternatively, get rid of your layers, and expose the EF objects directly:
var dtos = context.Employees.Where(e => e.IsActive)
.Project().ToArray<EmployeeShowViewModel>();
Put this in the controller because who cares, layers and abstractions are productivity preventers and time wasters.
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
For my project I use SqlCE 3.5, Linq2Sql and generate entity classes (dbml file) with SqlMetal.
If I use extensions of the entity classes as my business object classes, how shall I deal with child objects?
Can I use the existing EntitySet(Of ChildClass) that is already in the dbml file? Or should I create a new collection property in my partial class extension, like:
public partial class ParentClass
{
public List<ChildClass> children { get; set; }
}
I got this vague assumption that EntitySet(Of ChildClass) has some type of direct connection to the complete database table. And that I perhaps should use this as purely a data access object, and for business logic, I should keep this other collection object that can hold a subset of the table only.
But I'm not sure if I have totally misunderstood the concept? I really like to know how this should be used properly..?
EDIT1:
One thing that possibly is a performance issue for me is when binding my object collections to a datagridview. A few properties are updated quite frequently from a stock-exchange data feed, and I wonder if using the EntitySets as the business layer collection makes this slow. Though I do not call submitchanges on those updates. Still I'm not able to scroll the datagridview, due to all the updates. May this be due to using the EntitySets, or just the datagridview itself updating so frequently that scrolling will seem impossible?
If you are using the entities generated by the Designer, which are Proxy and not real POCO objects you can also use their ObjectSet to get the child colletions. They are of type IQueryable so until you touch one of the item or you use a ToList() or .Where() LinQ extension, EF won't call the database.
Anyway, the BEST PRACTICE is to expose them using an IList and use a Repository to fill the List. The ObjectSet is not only a List, it is also a sort of repository for the entity.