In my Custom ObjectContext class I have my entity collections exposed as IObjectSet so they can be unit-tested. I have run into a problem when I use this ObjectContext in a compiled query and call the "Include" extension method (From Julie Lerman's blog http://thedatafarm.com/blog/data-access/agile-entity-framework-4-repository-part-5-iobjectset/):
public IQueryable<MyPocoObject> RunQuery(MyCustomContext context, int theId)
{
var query = CompiledQuery.Compile<MyCustomContext, int, IQueryable<MyPocoObject>>(
(ctx, id) => ctx.MyPocoObjects.Include("IncludedPocoObject").Where(n => n.IncludedPocoObject.id == id));
return query(context, theId);
}
LINQ to Entities does not recognize the method 'System.Linq.IQueryable1[MyPocoObject] Include[MyIncludedPocoObject](System.Linq.IQueryable1[MyPocoObject], System.String)' method, and this method cannot be translated into a store expression.
If I use this same query on ObjectSet collections rather than IObjectSet it works fine. If I simply run this query without precompiling it works fine. What am I missing here?
I'm sure I've just misunderstood as you seem to have been looking at this for a while - But shouldn't you be including the ObjectSet not the object that is queried BY that object set.
eg:
var query = CompiledQuery.Compile<MyCustomContext, int, IQueryable<MyPocoObject>>(
(ctx, id) => ctx.MyPocoObjects
.Include("IncludedPocoObjectSET")
.Where(n => n.IncludedPocoObject.id == id));
Can you also confirm that running the same query without compiling doesn't throw the "can't translate" exception?
Related
Tech:
EF Core 2.0.0
Asp.Net Core Mvc 2.0.0
When i execute this method it throws "InvalidOperationException: Sequence contains no matching element" on evaluating CurrentGrade. Why does it throw and how can I fix it?
I have a search method filtering on quite a lot of properties on a large dataset (10 000 users with thousands of related entities). I'm trying to optimize the query and i don't want to execute the query until all filtering has been made. While using ToList() makes the method work i would rather work against an IQueryable and execute the query when done filtering.
I'm pretty sure this worked before updating EF Core from 1.x to 2.0.
public MemberQueryResult Search(MemberQuery filter)
{
var query = Context.Users
.Include(x => x.Honours)
.Include(x => x.Grades)
.Include(x => x.Strokes)
.Include(x => x.Posts)
.Include(x => x.Loge)
.AsNoTracking();
query = query.ApplyFiltering(filter);
return result;
}
ApplyFiltering() works well for filtering on foreign keys but when filtering on a navigation property collection using .Where() it throws on ICollection Grades on Member when just before the filtering it was included.
This is the method inside ApplyFiltering() that throws:
private static IQueryable<Member> SearchByCurrentGradeRange(MemberQuery filter, IQueryable<Member> result)
{
if (filter.GradeRange == null) return result;
var gradeRange = filter.GradeRange.Split(',');
var gradeFrom = (Grade)int.Parse(gradeRange[0]);
var gradeTo = (Grade)int.Parse(gradeRange[1]);
result = result.Where(x => x.CurrentGrade >= gradeFrom && x.CurrentGrade <= gradeTo);
return result;
}
CurrentGrade is a calculated property on a member, Grade is just an enum.:
public sealed class Member : IdentityUser
{
public Grade CurrentGrade => Grades.OrderBy(x => x.Grade).Last(x => x.ReceivedDate != null).Grade;
public ICollection<MemberGrade> Grades { get; set; } = new Collection<MemberGrade>();
}
The problem is that the unmapped ("calculated") property is causing client evaluation, but at the time EF evaluates the client part of the Where clause, the navigation properties are not loaded yet, hence your Grades collection is empty (as it has been iinitialized with new Collection<MemberGrade> - if you remove the initializer, then you'll get NullReferenceException).
Now, probably it could be treated as EF Core bug. But I strongly recommend not using unmapped properties in LINQ queries in general, and especially in query filter conditions. Even if they work, the client evaluation will cause loading a lot of data in memory just to apply the filter there, rather than at the database (SQL) side.
Also make sure to use SQL translatable constructs. For instance, Last / LastOrDefault have no natural SQL translation, while FirstOrDefault does, so the usual pattern is OrderByDescending().FirstOrDefault() rather than OrderBy().LastOrDefault().
With that being said, the working server side evaluating solution in your case would be like this:
result = result.Where(m => m.Grades
.Where(x => x.ReceivedDate != null).OrderByDescending(x => x.Grade).Take(1)
.Any(x => x.Grade >= gradeFrom && x.Grade <= gradeTo));
So am using AspNetCore 1.0 with EFCore 1.0, both latest releases as far as I am aware.
Executing a query to delete an object using the FromSql method on a DbSet throws an exception. Both the code and exception are below.
public void DeleteColumn(int p_ColumnID)
{
int temp = p_ColumnID;
string query = "DELETE FROM Columns WHERE ID = {0}";
var columnsList = m_context.Columns.FromSql(query, p_ColumnID).ToList();
foreach (Columns c in columnsList)
{
m_context.Columns.Remove(c);
}
m_context.SaveChanges();
}
After executing the FromSql call, I get the following exception
An exception of type 'System.NotSupportedException' occurred in Remotion.Linq.dll but was not handled in user code
Additional information: Could not parse expression 'value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[ASPNET5_Scrum_Tool.Models.Columns]).FromSql("DELETE FROM Columns WHERE ID = {0}", __p_0)': This overload of the method 'Microsoft.EntityFrameworkCore.RelationalQueryableExtensions.FromSql' is currently not supported.
I have no clue how to fix this error and from Googling I have come across no similar problems.
I am also wondering, if the query/code was successful it would return an 'IQueryable object. Would that solely contain the results of the query, in this case the specific Column object to delete?
FromSql is intended to allow you to compose a custom SQL SELECT statement that will return entities. Using it with a DELETE statement is not appropriate here, since your goal is to load the records you want to delete and then delete them using the default Entity Framework mechanism. A Delete statement generally does not return the records deleted (though there are ways to accomplish that). Even if they did, the records will already be deleted and so you won't want to iterate over them and do a Remove on them.
The most straightforward way to do what you want might be to use the RemoveRange method in combination with a Where query.
public void DeleteColumn(int p_ColumnID)
{
m_context.Columns.RemoveRange(m_context.Columns.Where(x => x.ID == p_ColumnID))
m_context.SaveChanges();
}
Alternately, if you want to load your entities and iterate manually through them to
public void DeleteColumn(int p_ColumnID)
{
columnList = m_context.Columns.Where(x => x.ID == p_ColumnID);
foreach (Columns c in columnsList)
{
m_context.Columns.Remove(c);
}
m_context.SaveChanges();
}
If you really want to issue the Delete statement manually, as suggested by Mike Brind, use an ExecuteSqlCommand method similar to:
public void DeleteColumn(int p_ColumnID)
{
string sqlStatement = "DELETE FROM Columns WHERE ID = {0}";
m_context.Database.ExecuteSqlCommand(sqlStatement, p_ColumnID);
}
I had the same exception in a case where I did not use delete statement. Turns out I was using the In-Memory Database. Since it is not a real database you can't use FromSQL.
I have some code in a repository base class for Entity Framework that eager loads Navigation properties:
public virtual List<T> Find(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties)
{
//blah biddy blah blah
}
Then when calling the above method:
var beers = BeerRepository.Find(x => x.Type == "IPA", a => a.Ingredients, b => b.Sizes, c => c.Hangovers);
It works great. I know that using "params" provides a great magic shortcut when calling the method and I've seen some SIMPLE examples of what would be needed without it.
But, I'm having trouble figuring out how to call the method above when I remove params from the signature.
Any thoughts?
A generic method is a method template. If you supply a type argument, it becomes a concrete, typed, method. Your method (without params)...
public virtual List<T> Find<T>(Func<T, bool> where,
Expression<Func<T, object>>[] navigationProperties)
...in BeerRepository will turn into something like...
public virtual List<Beer> Find(Func<Beer, bool> where,
Expression<Func<Beer, object>>[] navigationProperties)
...which clearly shows you have to provide a Expression<Func<Beer, object>>[] array. It takes a bit more clunky code to build that, because you can't take advantage of type inference:
var navProps = new Expression<Func<Beer, object>>[]
{
a => a.Ingredients,
a => a.Sizes,
a => a.Hangovers
});
Not sure I understand your question.
You can just call
beerRepository.Find((x => x.Type == "IPA")
And then inside Find() you'll see that the navigationProperties array will be empty.
I'm having a problem when use AutoMapper
my code:
public static IQueryable<IGrouping<Int64, GroupTimeSheetViewModel>> ToListGroupViewModel(this IQueryable<IGrouping<Int64, GroupTimeSheet>> entity)
{
return Mapper.Map<IQueryable<IGrouping<Int64, GroupTimeSheet>>, IQueryable<IGrouping<Int64, GroupTimeSheetViewModel>>>(entity);
}
when I run my code, I get error:
The value "System.Collections.Generic.List1[PG.Admin.Models.TimeSheetHeaders.GroupTimeSheetViewModel]" is not of type "System.Linq.IGrouping2[System.Int64,PG.Admin.Models.TimeSheetHeaders.GroupTimeSheetViewModel]" and cannot be used in this generic collection.
Parameter name: value
AutoMapper can't map queryables directly. If I were you, I'd use AutoMapper's LINQ projection capabilities. It uses LINQ to build out the Select projection. That way, your resultant LINQ would be something like:
dbContext.GroupTimeSheets
.GroupBy(ts => ts.Something)
.Project().To<GroupTimeSheetViewModel>()
.ToListAsync();
I have the following uninformative (in my opinion) error message:
System.NotSupportedException was unhandled by user code
Message='Company.Data.DataProvider.Schema.DerivedType' is not a valid metadata type for type filtering operations. Type filtering is only valid on entity types and complex types.
I have the following Tables in my database:
BaseType
ObjectID uniqueidentifier
.. other unrelated columns
DerivedType
ObjectID unqueidentifier
UserID unqueidentifier
.. other unrelated columns
I have the following classes used by Entity Framework using Inheritance:
BaseType
Guid ObjectID
.. other unrelated properties
DerivedType : BaseType
Guid UserID
.. other unrelated properties
My Partial SchemaContext looks like:
public partial class SchemaContext
{
public IQueryable<DerivedType> DerivedTypes
{
get { return this.BaseType.OfType<DerivedType>()
}
}
I have a method that looks like:
result = this._dbContexts.SchemaContext.DerivedTypes
.Include(i => i.RatingType)
.Include(i => i.ObjectVersion)
.Where(dt => objectIDs.Any(id =>
dt.ObjectVersion.
.MatchingObjects
.Select(s => s.ObjectID).Contains(id))
&& dt.UserID == userID)
.ToList();
I believe the .OfType<> is throwing the error, and I cannot use the base type because I need to filter on the UserID. Any suggestions on solving this problem?
I believe this error message is indicationg that your derived type is not properly defined in the Entity Model. If using a POCO/Context generator that manipulates entity object type names, verify that the name of your objects matches.