The question was originally asked at http://entityframework.codeplex.com/discussions/399499#post928179 .
Good day! Please tell me if it is wrong place to post this question.
I have a query as follows:
IQueryable<Card> cardsQuery =
dataContext.Cards
.Where(predicate)
.OrderByDescending(kc => kc.SendDate)
.AsQueryable();
Then I try:
Task<Card[]> result = cardsQuery.ToArrayAsync();
And then exception rises:
The source IQueryable doesn't implement IDbAsyncEnumerable<Models.Card>
I use modified version of 'EF 5.x DbCotext generator'.
How to avoid it?
UPDATE
Important remark is that I have method to produce IQuerayble<Card> as follows:
class Repository {
public IQueryable<Card> GetKudosCards(Func<Card, bool> predicate) {
IEnumerable<KudosCard> kudosCards = kudosCardsQuery.Where(predicate);
return kudosCards
.OrderByDescending(kc => kc.SendDate)
.AsQueryable();
}
}
What is the point of calling AsQueryable? If you compose a query with the extension methods starting from an IQueryable source collection (e.g. DbSet, ObjectSet), the query will be IQueryable too.
The purpose of AsQueryable is to wrap an IEnumerable collection with an IQueryable proxy/adapter that uses a Linq provider that is capable of compiling IQueryable queries into a Linq to Object queries. This can be useful in scenarios when you would like to use inmemory data queries.
Why is the AsQueryable call necessary? What if you just simply remove it?
Update
Okey, now it seems I understand your problem. After a quick look on the ODataQueryOptions.ApplyTo I realized that it just extends the underlying expression tree of the query. You can still use it to run the query in the way you want, however you need a little trick to transform the query back to generic.
IQueryable<Card> cardsQuery =
dataContext.Cards
.Where(predicate)
.OrderByDescending(kc => kc.SendDate);
IQueryable odataQuery = queryOptions.ApplyTo(cardsQuery);
// The OData query option applier creates a non generic query, transform it back to generic
cardsQuery = cardsQuery.Provider.CreateQuery<Card>(odataQuery.Expression);
Task<Card[]> result = cardsQuery.ToArrayAsync();
The problem is as follows.
I have a method:
class Repository {
public IQueryable<Card> GetKudosCards(Func<Card, bool> predicate) {
IEnumerable<KudosCard> kudosCards = kudosCardsQuery.Where(predicate);
return kudosCards
.OrderByDescending(kc => kc.SendDate)
.AsQueryable();
}
}
The problem is that kudosCards has type IEnumerable<KudosCard>. That throws exception. If I change predicate type to Expression<Func<Card, bool> predicate then everything works just fine.
I had the same problem when I was using the LinqKit library expression builder, which in the end, was producing AsQueryable(), and very surprisingly it was happening for me from the XUnit Integration Tests call.
I was going wild about why the same problem wasn't happening when calling the same API endpoint via Swagger.
It turned out I had to do an elementary change.
I had to replace:
using System.Data.Entity;
with:
using Microsoft.EntityFrameworkCore;
I had the same problem when I was using the LinqKit library expression builder, which in the end, was producing AsQueryable().
And very surprisingly, it was happening for me from the XUnit Integration Tests call.
I was going wild about why the same problem wasn't happening when calling the same API endpoint via Swagger.
It turned out I had to do an elementary change.
I had to replace the:
using System.Data.Entity;
with:
using Microsoft.EntityFrameworkCore;
In the place where I was calling the LinqKit expression method.
If you want to use the List<T> for Linq query, Avoid chaining it with ToListAsync(). It uses the class IDbAsyncEnumerable that is not available to a normal List<T> object
Related
I am using entity framework and I want to execute a query and would like to know which is the besr way to execute a query. Which is best practice and why, and which one is more per-formant.
Option 1)
return
this.Storage.Customer.OfType<Preferred>()
.Include("Order")
.Where("it.Id = #customerId AND it.CustomerType = #cusType", new ObjectParameter("customerId ", customerId), new ObjectParameter("cusType", (int)cusType))
.Execute(MergeOption.OverwriteChanges)
.SingleOrDefault();
OR
return
this.Storage.Customer.OfType<Preferred>()
.Include(b => b.Order)
.Where(cust => cust.Id == customerId && cust.CustomerType== (int)cusType)
.SingleOrDefault();
The second question is why in option 2 us the .Execute not available? It appears red.
Thanks in advance.
The performance difference should be negligible compared with the actual data access, but you need to measure it to determine for sure.
Include with a lambda just uses reflection to get the property name then calls the version with a string parameter, so the only overhead is parsing the expression. (This is an implementation detail, however, so it is subject to change)
The benefit of using a lambda is type safety - using the wrong property name in a lambda will break the build, but using the wrong string will only fail at run-time.
The reason Execute is not available is because Include with a lambda parameter is an extension method on IQueryable<T> that returns an IQueryable<T> in order to chain methods like Where. Include with a string parameter is a method on ObjectQuery<T> that returns an ObjectQuery<T>. Execute is a method on ObjectQuery<T>, not IQueryable<T> so it is not available when you use IQueryable methods.
Plot:
I have a class implemented as a facade around Entity Framework DB context. It developed to maintain backward compatibility, it mimics class with same public interface, but uses DTOs instead of EF entities.
Problem:
I have next method inside class described above. See code below:
public IQueryable<T> FindBy<T>(Expression<Func<T, Boolean>> predicate) where T : BaseDto {
//GetDestinationType takes source type of some declared mapping and returns destination type
var entityType = Mapping.Mapper.GetDestinationType(typeof (T));
var lambda = Expression.Lambda(predicate.Body, Expression.Parameter(entityType));
// dbContext declared as class field and initialized in constructor
var query = dbContext.Set(entityType).Where(lambda); // <-- Cannot use non-generic expression/lambda
return query.ProjectTo<T>(mapper.ConfigurationProvider); }
I need to take Expression using DTOs as In parameter and return IQueryable where T : BaseDto as a result
I need to convert input predicate to the same predicate using EF entities as In parameter
I need to filter non-generic EF DbSet with help of dynamically created Expression (predicate)
Main question
Is it possible to filter non-generic EF DBSet with help of dynamically created Expression (predicate)
Please give me some glue or further directions if I need to use some other approach.
Problem has been solved. Solution was quite obvious. Instead of
var query = dbContext.Set(entityType).Where(lambda);
I can write
var query = dbContext
.Set(entityType)
.ProjectTo<T>(mapper.ConfigurationProvider)
.Where(predicate);
where predicate is the input parameter of FindBy() method.
This code will be successfully compiled and, what is more important, EF will build optimal query to the Database which will include Where() clause in the query body, so it won't take full set of records from DB side.
We have a multi-layered application, where all the repositories are based on a (home-grown) GenericRepository base class (where T is an entity in the model), that exposes methods such as GetContext(), GetObjectSet() and so on. We allow the repositories that inherit from this to access the context, as they need to call Include(), as we are passing the data up through a WCF service, so need to load all related entities eagerly.
All of our entities implement an interface that has an Active bool property, and what we want to do is intercept the execution of a query, and filter on the Active property, so that any query only returns entities where this is set to true.
Can this be done? In Lightswitch, which is built on EF, there is an event you can capture that gets fired right down in the depths of the query execution, and allows you to do this sort of filtering. I can't find anything in EF itself that allows this.
Anyone any ideas? Thanks
In EF 5, Include is an extension method on IQueryable, so you can do this:
var query = dbSet.Where( o => o.IsActive ).Include( ... )
That means, you don't have to return a DbSet<T> from your generic repository - it should be ok to return an IQueryable<T>.
If this meets your requirements, you can add a Where clause to your generic repository method:
partial class GenericRepository<T>
{
public IQueryable<T> Query( bool includeInactive = false )
{
return ctx.Set<T>().Where( o => includeInactive || o.IsActive );
}
}
The best way that I can think of doing that would be to have your repository methods take in an Expression (i.e. Expression<Func<T, bool>> predicate). That way you can do all of your queries in the actual repository itself (and thus not allowing client-side code any way of accessing your data layer logic) to which you can add a Where before you return from the repository method to only grab those that are Active.
An example of this style that I've used is the following:
public IQueryable<T> Grab(Expression<Func<T, bool>> predicate)
{
return DbSet.Where(predicate);
}
Where DbSet is the actual table that you're trying to query. That way you can add .Where(x => x.Active) to the end of it, have it not execute against the database yet (thank you, deferred execution!) and yet still get the exact records that you're looking for.
Basically I'd be looking to implement a method like this.
IQueryAble GetQuery<T>(Entities db) or extension method Entities.GetQuery<T>()
This way you could do things like this
public IQueryable<T> GetAll()
{
return yourEntityClasses.GetQuery<T>();
}
which would return a SELECT * FROM query expression and obviously from there you could make addtional generic methods for sorting, pagination, where expressions, etc on top of this would save you from having to repeat the code for these methods for each table. I know SubSonic3 does a very good job of this, but was trying to duplicate some of the functionality in an EntityFramework project I am working on. Only thing I see in EF is CreateQuery and ObjectQuery but both of those require you to pass a querystring in which would require you to know the table name.
Well it is possible to get the string that you need to put into the CreateQuery method automatically.
Basically it is just string.Format("[{0}]",entitySetName);
How do you get the entity set name, assuming you have don't use something called MEST, most people don't, you can use some a functions I wrote in this Tip to get the EntitySet for T and from that the name.
Once you do that it should be pretty trivial for you to write the Extension Method
i.e.
public static IQueryable<T> GetAll<T>(this ObjectContext context)
{
var wkspace = context.MetadataWorkspace;
EntitySet set = wkspace
.GetEntitySets(wkspace.GetCSpaceEntityType<T>())
.Single();
return context.CreateQuery<T>(string.Format("[{0}]",set.Name);
}
See the above tip for the other functions used.
Hope this helps
Alex
I am using the entity framework and I'm having a problem with "re-finding" objects I just created... basically it goes like this:
string theId = "someId";
private void Test()
{
using(MyEntities entities = new MyEntities())
{
EntityObject o = new EntityObject();
o.Id = theId;
entities.AddToEntityObject(o);
CallSomeOtherMethod(entities);
}
}
void CallSomeOtherMethod(MyEntities ents)
{
EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId);
if(search == null)
{
Console.WriteLine("wha happened???");
}
}
(no guarantee the code works btw - it's all from my head)
Why doesn't the query "find" the EntityObject that was just created?
If I call SaveChanges() after the AddToEntityObject it works (which doesn't surprise me) but why doesn't it pull from the cache properly?
I'm still green on this stuff so I'm hoping that there's some really easy thing that I'm just overlooking...
Thanks
The newly added object is in the local DataSource, since it's not persisted yet in the database,
so you may say:
EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ??
ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);
This happens because ents.EntityObject.WhatEver always queries the datasource. This is a design decision. They do it this way, because else they would have to execute the query against the datasource, against the local cache and then merge the results. As one of the developers pointed out in a blog (cannot remember where exactly) they were unable to handle this consistently.
As you can imagine there are a lot of corner an edge cases you have to handle properly. You could just find a id you created locally, created by someone else in the database. This would force you to be prepared to handle conflicts on (almost) every query. Maybe they could have made methods to query the local cache and methods to query the datasource, but that is not to smart, too.
You may have a look at Transparent Lazy Loading for Entity Framework. This replaces the normal code generator and you get entities that populate their related entity collections and entity references automatically on access. This avoids all the
if (!Entity.ReleatedEntities.IsLoaded)
{
Entity.RelatedEntities.Load();
}
code fragments. And you can query the collections because they are always implicitly loaded. But this solution is not perfect, too. There are some issues. For example, if you create a new entity and access a collection of related entities, you will get an exception because the code is unable to retrieve the related entities from the database. There is also an issue concerning data binding and may be some more I am not aware of.
The good thing is that you get the source code and are able to fix the issues yourself and I am going to examine the first issue if I find some time. But I am quite sure that it will not be that easy to fix, because I expect some case were just not hitting the database if the entity has just been created is not the expected behavior.
I was in the same situation. I wrote this extension method that at least for me solves the problem (I don't have issues with i.e conflicts in my context...)
public static IEnumerable<T> WhereInclAdded<T>(this ObjectSet<T> set, Expression<Func<T, bool>> predicate) where T : class
{
var dbResult = set.Where(predicate);
var offlineResult = set.Context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(entry => entry.Entity).OfType<T>().Where(predicate.Compile());
return offlineResult.Union(dbResult);
}
The extension method bellow is to DbSet<>
public static T TryAttach<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate) where T : class
{
T found = dbSet.Local.SingleOrDefault(predicate.Compile());
if (found == null) dbSet.Attach(entity);
return found ?? entity;
}
How to use:
contextInstance.MyEntity.TryAttach(entityInstance, e => e.ID == entityInstance.ID);
btw: I love generics!
I have recently struggled with this same question. I'm posting this answer 2 years after the question was asked in hopes that this bit of code may help someone searching for an answer.
I have basically implemented an extension method (as suggested by Alex James) called "Find" that operates in the same way that "Where" does, but "Find" also checks the ObjectContext to see if there are any Added entities that satisfy the given predicate. This allows you to find an entity even if it hasn't been saved to the database yet.
Find returns an IQueryable(of T) so that you can use it just like any other LINQ operator.
<Extension()>
Public Function Find(Of T As Class)(ByVal OSet As ObjectSet(Of T), _
ByVal predicate As Expression(Of Func(Of T, Boolean))) _
As System.Linq.IQueryable(Of T)
'Check the object context for Added objects first.
Dim AddedContextObjects = OSet.Context.ObjectStateManager _
.GetObjectStateEntries(EntityState.Added) _
.Select(Function(entity) entity.Entity).OfType(Of T)()
Dim Cpredicate = predicate.Compile
Dim MatchingObjects As New List(Of T)
For Each TObj As T In AddedContextObjects
If Cpredicate.Invoke(TObj) Then
MatchingObjects.Add(TObj)
End If
Next
'Now include a query to retrieve objects from the DB.
Dim DBObjects = OSet.Where(predicate)
If MatchingObjects.Count > 0 Then
'We found some added objects in the context.
'We want to return these objects as well as any Objects in DB
'that satisfy the predicate.
Return MatchingObjects.Union(DBObjects).AsQueryable
Else
'We didn't find any added objects in the context,
'so we just return the DB query.
Return DBObjects
End If
End Function
You have a number of options. You could extend the ObjectContext with another partial class to make your own mechanism for retrieving recently Added information.
Or you could just put an extension method on the ObjectContext that looks through the ObjectContext.ObjectStateManager looking for 'added' ObjectStateEntries, and then use LINQ to Objects to find what you are looking for.
Entity Framework 6
As per EF Docs Dbset always query against the database.
Note that DbSet and IDbSet always create queries against the database
and will always involve a round trip to the database even if the
entities returned already exist in the context. A query is executed
against the database when:
It is enumerated by a foreach (C#) or For Each (Visual Basic)
statement. It is enumerated by a collection operation such as ToArray,
ToDictionary, or ToList. LINQ operators such as First or Any are
specified in the outermost part of the query. The following methods
are called: the Load extension method on a DbSet,
DbEntityEntry.Reload, and Database.ExecuteSqlCommand. When results are
returned from the database, objects that do not exist in the context
are attached to the context. If an object is already in the context,
the existing object is returned (the current and original values of
the object's properties in the entry are not overwritten with database
values).
When you perform a query, entities that have been added to the context
but have not yet been saved to the database are not returned as part
of the result set. To get the data that is in the context, see Local Data
If a query returns no rows from the database, the result will be an
empty collection, rather than null.
Below is a simple snippet with local data:
await dbContext.Entity
.Where(e => e.Title.Contains("Text"))
.LoadAsync();
var locaEntities = dbContext.Entity.Local;
dbContext.Entity.Add(new Entity {});
// call save post atomic operation is finished.
await dbContext.SaveChangesAsync();