-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query: "The EF.Property<T> method may only be used within LINQ queries" and unnecessary client-side evaluation triggered in certain generic usages #4875
Comments
Some additional information and alternative repro steps in #3837. |
Working on a customer case today I learned that the compiler won't always introduce a convert to private IQueryable<T> ApplyTenantFilter<T>(IQueryable<T> source) where T : IHasTenant
=> source.Where(e => e.TenantId == _tenantId); (Note that T is constrained to the interface but not to be a reference type) Then the compiler would introduce convert nodes to the interface everywhere. I am reopening so that we can validate if our fix would work for that. At a first glance it seems we only considered object, but perhaps this works for other reasons. |
This works correctly in the current bits. SELECT DISTINCT [e].[Country]
FROM [Customer] AS [e]
WHERE [e].[TenantId] = @___tenantId_0 |
I'm still having this issue in my class: using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace emsnet.Code
{
public class PaginatedList<T> : List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set; }
public int PagesDisplay { get; private set; }
public int PageStart { get; private set; }
public int PageEnd { get; private set; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize, int pagesDisplay)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
PagesDisplay = pagesDisplay >= TotalPages ? TotalPages : pagesDisplay;
if ((pageIndex - 1) == 0)
{
// If it is the first run, or pull back
PageStart = 1;
PageEnd = PagesDisplay;
}
else if ((pageIndex >= PageStart && pageIndex <= PageEnd) == false)
{
// Check if it is greater or lesser
if (PageIndex > PageEnd)
{
// If pageIndex is greather than the actual range
PageStart = (PageEnd + 1);
PageEnd = PageStart + +(PagesDisplay - 1);
}
else if (PageIndex < PageStart)
{
// If oageIndes is lesser than the actuial range
PageEnd = PageIndex;
PageStart = (PageEnd - PagesDisplay) + 1;
}
}
this.AddRange(items);
}
public bool HasPreviousPage
{
get
{
return (PageStart > 1);
}
}
public bool HasNextPage
{
get
{
return (PageEnd < TotalPages);
}
}
public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize, int pagesDisplay)
{
var count = await source.CountAsync();
**ERROR AT THIS LINE**: var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
return new PaginatedList<T>(items, count, pageIndex, pageSize, pagesDisplay);
}
}
} When I pass the parameters var ret = _context.AppMenus
.Where(m => m.ParentMenuId != 0 && m.Title.Contains(searchString))
.Select(m => new MenuList
{
MenuId = m.MenuId,
Title = m.Title, Selected = (m.AppProgramMenus.Where(
p => p.ProgramId == programid && p.MenuId == m.MenuId).Count() > 0) }); What am I doing wrong? Regards |
@wgutierrezr What version of EF Core are you using? |
Hi, I'm using EF Core 1.1.2 |
This is the action where I get the data from the DB. I am sorting the resulting query. public async Task<IActionResult> Index(string sortField, string searchString, string CurrentSortField, string sortOrder, int? page, int? roleid)
{
bool descending = false;
// check if null then take the first role on the table order alphabetically
roleid = roleid ?? _context.AppRoles.OrderBy(p => p.Title).First().RoleId;
ViewData["currentRoleId"] = roleid;
// Check if it is the first run or if click over the same column
if (sortField == CurrentSortField && CurrentSortField != null)
{
descending = (sortOrder ?? "D") == "A" ? true : false;
ViewData["sortOrder"] = descending ? "D" : "A";
}
else
{
ViewData["sortOrder"] = "A";
sortField = sortField ?? "Title";
}
ViewData["CurrentSortField"] = sortField;
if (searchString != null)
{
page = 1;
}
else
{
searchString = "";
}
ViewData["currentFilter"] = searchString;
// Get the current menu-programs assigned to a particular Role
var ret = _context.AppRoleMenuPrograms.Where(p =>
p.RoleId == roleid &&
(p.ProgramMenu.Program.Title.Contains(searchString) || p.ProgramMenu.Menu.Title.Contains(searchString)))
.Select(p =>
new RolMenuProgram
{
Id = p.ProgramMenu.ProgramMenuId,
ProgramTitle = p.ProgramMenu.Program.Title,
MenuTitle = p.ProgramMenu.Menu.Title,
canCreate = p.AllowCreate,
canRead = p.AllowRead,
canUpdate = p.AllowUpdate,
canDelete = p.AllowDelete
});
if (descending)
{
> I think using this order by generates the problem (EF.Property<object>(e, sortField))
ret = ret.OrderByDescending(e => EF.Property<object>(e, sortField));
}
else
{
ret = ret.OrderBy(e => EF.Property<object>(e, sortField));
}
ViewData["Roles"] = new SelectList(_context.AppRoles.OrderBy(p => p.Title).ToList(), "RoleId", "Title", roleid);
return View(await PaginatedList<RolMenuProgram>.CreateAsync(ret, page ?? 1, 25, 10));
} |
In certain usages of the
EF.Property()
method the compiler introduces a cast toobject
for theentity
argument of the call. This apparently prevents EF Core from correctly parsing the LINQ expression.I have seen this lead to two different outcomes:
I was able to produce a relatively small repro for the exception case (see below) but unfortunately the second effect is more elusive and so far only appears in a larger application (I can work with someone figuring out that part).
Note that the cast to
object
is added by the compiler to account for value types which need to be boxed in order to be passed as objects, so adding the generic constraintwhere T: class
toApplyTenantFilter()
prevents the compiler from introducing the cast. However that doesn't seem to be a very discoverable workaround.The text was updated successfully, but these errors were encountered: