问题
I write app using .Net 5 and Entity Freamwork Core. I have method that is looking for some data. It looks like this:
public async Task<Result<List<Company>>> Search(string keyword, DateTime From, DateTime To, JobTitle jobTitle)
{
return Result.Ok(await _dataContext.Companies
.Include(x => x.Employes
.WhereIf(From != default && To != default,
x => x.DateOfBirth >= From && x.DateOfBirth <= To && x.JobTitle == jobTitle))
.WhereIf(!string.IsNullOrEmpty(keyword),
x => x.Name.Contains(keyword))
.ToListAsync());
}
WhereIf looks like this:
public static class LinqExtension
{
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, bool>> predicate)
{
if (condition)
return source.Where(predicate);
return source;
}
public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, bool> predicate)
{
if (condition)
return source.Where(predicate);
return source;
}
}
And when I use this method I got this error:
System.InvalidOperationException: The expression 'x.Employes.WhereIf(__p_0, x => (((x.DateOfBirth >= __From_1) AndAlso (x.DateOfBirth <= __To_2)) AndAlso (Convert(x.JobTitle, Int32) == Convert(__jobTitle_3, Int32))))' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty'). Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data
How resolve this error?
回答1:
Answer is simple. EF Core do not invoke custom methods in Include
definition. Which is ok, because this method is not IQueryable method.
So just rewrite your code:
public async Task<Result<List<Company>>> Search(string keyword, DateTime From, DateTime To, JobTitle jobTitle)
{
IQueryable<Company> query = _dataContext.Companies;
if (From != default && To != default)
query = query.Include(x => x.Employes
.Where(x => x.DateOfBirth >= From && x.DateOfBirth <= To && x.JobTitle == jobTitle));
else
query = query.Include(x => x.Employes);
query = query.WhereIf(!string.IsNullOrEmpty(keyword), x => x.Name.Contains(keyword));
return Result.Ok(await query.ToListAsync());
}
Or you can use additional extension for includes:
public static class LinqExtension
{
public static IIncludableQueryable<TEntity, IEnumerable<TProp>> IncludeFiltered<TEntity, TProp>(
this IQueryable<TEntity> source, bool condition,
Expression<Func<TEntity, IEnumerable<TProp>>> selector,
Expression<Func<TProp, bool>> predicate)
where TEntity : class
{
if (condition)
{
var newBody = Expression.Call(typeof(Enumerable), "Where", new[] {typeof(TProp)},
selector.Body,
predicate);
selector = Expression.Lambda<Func<TEntity, IEnumerable<TProp>>>(newBody, selector.Parameters);
}
return source.Include(selector);
}
}
public async Task<Result<List<Company>>> Search(string keyword, DateTime From, DateTime To, JobTitle jobTitle)
{
return Result.Ok(await _dataContext.Companies
.IncludeFiltered(From != default && To != default,
x => x.Employes, x => x.DateOfBirth >= From && x.DateOfBirth <= To && x.JobTitle == jobTitle)
.WhereIf(!string.IsNullOrEmpty(keyword),
x => x.Name.Contains(keyword))
.ToListAsync());
}
来源:https://stackoverflow.com/questions/65106895/how-to-resolve-error-with-enitiy-freamwork-include