I'm developing a database application using Entity Framework 6. In one place, I need to assemble a query based on user input and then return that query to the caller. I can't run the query myself and return the result set because the caller needs to store the query to run again later if the UI is refreshed. I also can't return an IQueryable representing the assembled query because this is only valid for the context against which it was assembled.
I've looked at using CompiledQuery.Compile but this doesn't work with the DbContext base class in Entity Framework 6. I need to do something like that, pre-compiling/pre-assembling/pre-constructing a query that can be returned and run again later, but that works with Entity Framework 6. I don't care whether the query is returned in the form of a delegate or whatever, I just need something that represents the query independently of any specific context instance.
How can I pre-construct and return a query with Entity Framework 6 such that it can be run multiple times against different context instances?
What I ended up doing was creating a delegate type to encapsulate the queries, that takes MyDatabaseContext as a parameter and returns a List<T> where T is the type of the particular entity that I'm querying for.
So now the function that creates the query can simply return a function and whenever the query needs to be run the query is simply called as a function passing it the context instance to act upon as a parameter. The function returns from the given context a list of entities as applicable for the query that it represents.
Related
I am new to Entity Framework and Linq, I would like to understand how dbset/dbcontext works when executing the following kind of LINQ request:
from x in db.Products select x
db is the data context object, Products the dataset.
Is the search/loading of these records done directly at the base table level or is a first search done on the dbset? And then we complete and retrieve the records that are not yet tracked/in the dbset?
In other words what is the path of the loading of these elements?
Thank you,
Is the search/loading of these records done directly at the base table level or is a first search done on the dbset?
It's done so at the "base table level" in the Database Server by receiving the results directly from the query and then DataContext will return any existing entity that it already have track of and create new entities if it doesn't have any track for those records.
And then we complete and retrieve the records that are not yet tracked/in the dbset?
The DataContext will create new entities for those records and it'll be tracked if you didn't explicitly specify AsNoTracking.
In other words what is the path of the loading of these elements?
The way it works from this document is that when you make a LINQ Query like this:
from x in db.Products select x
It will generates a LINQ Expression and then pass that expression to the Database Provider to generate the actual database query specific to the database engine it's made for (it may not have all of the query compiled, so some of the query may be computed from the application side.)
It will then execute the query and receive the result from that query and if the query is made with tracking, then it'll return any existing entity that the DataContext already have track of and create new entities if not.
The entity and record will be tied by the key and whenever there is any part of the query using Keyless Entity Type, the whole query would be made as a NoTracking query.
Note that if the database record for such existing entity have changed, it will not update the values in the existing entity, you will have to manually reload that entity like so:
db.Entry(product).ReloadAsync();
When using EF6 Database First and trying to execute a stored procedure the auto-generated context adds the required method but sets the return type as ObjectResult. For example the following definition:
public virtual ObjectResult<USP_GetItemDetails_Result> USP_GetItemDetails(int? itemNbr, int? siteNbr)
Has the return type of:
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<USP_GetItemDetails_Result>("USP_GetItemDetails", itemNbr, siteNbr);
Is there anyway to materialize this result as just the concrete class rather than having to send the result as ObjectResult? The next layer in my stack is not aware of Entity Framework.
Turns out ObjectResult under the hood is an IEnumerable. A simple toList means I can return an IEnumerable to the next layer.
I am writing a Pager so that all my Queries (either DB or List Based) can use the same logic.
this seems to work correctly for functions like StartsWith when the data type is a System.Collections.Generic.List<T> but when the query comes from a DB Store (EF) it complains as i am setting the StringComparison property (EF does not implement the method signature with StringComparision)
What I have done is write 2 seperate functions 1 for normal list (with StringComparison and one for a DB Connected Query
My Question is: What is the best way to see if the Query is from a Data Store (without actually referencing the EF Modules as i dont want to be tied to EF).
This is what i currently have that works:
bool _queryConnectedToData=query.Provider.GetType().Namespace.ToLower().Contains("system.data");
Try using ObjectContext to accomplish this
ObjectContext.GetObjectType(query.GetType());
For entity this will return your type of Entity, and with a list it'll still return your System.Collections.Generic.List<T>
It is tied to System.Data.Entity, but if you don't want it tied to that then inject it through an interface.
I have two tables, Kittens and Owners in my Entity Framework model. A Kitten has 1 Owner but an Owner can have many Kittens.
I have a repository method called GetKittens() that returns _context.Kittens.ToList();
Since I set up an association, I can do kitten.Owner.Name.
But since ToList() was already called, and the context disposed of, how does it access the property? When retrieving an Entity, does it do a Join to all tables that have an association?
I have to write a query that pulls data from 4 tables, so I am wondering how to do this efficiently, hence this question trying to understand a bit more about how EF works.
By default, a DbContext will use lazy loading. There is a few options available to you, depending on your use cases.
1- If you have control over the lifetime of your DbContext, do not dispose it. However, every time you will access a related entity (for the first time), a new query will be sent to the database to fetch it.
2- Eagerly include the related entity by use Include on the IQueryable<Kitten>:
// For imagine context is the DbContext for your EF Model
context.Kittens.Include(c => c.Owners); // Or Include("Owners")
However, if you have no control over your repository, you have no option but to call a related method of your repository (like IEnumerable<Owner> GetOwners(Kitten kitten)) since the repository already returns the list.
If you do, consider either eagerly include the Kitten's owners in the repository before materializing with ToList() or return an IQuerable and leave the responsibility to the calling class to include related entities or customizing the query. If you do not want a caller to be able to alter the query, you can add an overload with includes that could be something along the line of:
public List<Kitten> GetKittens(params string[] includes)
{
return includes.Aggregate(
context.Kittens.AsQueryable(),
(query, include) => return query.Include(include)).ToList();
}
All in all, this is an implementation decision that you will have to take.
So I have an entitytype called SportsWagon and an interface ICar that SportsWagon implements. I've also created a function of type IEnumerable<ICar> GetCars(Func<ICar, bool> filter) that fetches the matching cars from the SportsWagons and returns them as an enumerable of cars. This function is very simple and only calls MyContext.SportsWagons.Where(filter).Where(someAdditionalLogicInSomeCases);
My problem is that, using SQL Profiler, the queries generated by this seem to be completely missing the WHERE-clause. What's worse, is that if I have a loop that fetches same cars multiple times, they seem to generate a new query every time. I'm using EF 4.3.1 database first. So is EF just unable to translate the interface-based Func to create the correct query with the database, or am I just doing something else that's totally silly?
I've also created a function ... filter that ...
...Where(filter)....
EF cannot translate a function to SQL. You'll have to provide a lambda.
To make sure, post the relevant code for how the function and the query fit together.