How do I achieve client-side evaluation in LINQ?

58 Views Asked by At

I don't understand the code formatting feature of StackOverflow.

I'm trying to do client-side evaluation, but it doesn't seem like it is taking. If you look in the output, it still has the object from the AsEnumerable query. Shouldn't it just be results? I don't understand why it's trying to write it in the query still. Is there another way to enable client-side evaluation?

var groups = Groups!.Where(g => g.Selected).Select(g => (Group)g.Object!).AsEnumerable();
SelectedGroupPosts.AddRange(from p in DB.Post
                            join u in DB.User
                               on p.UserId equals u.Id
                            join gm in DB.GroupMember
                                on u.Id equals gm.UserId
                            join g in DB.Group
                                on gm.GroupId equals g.Id
                            where groups.Contains(g)
                            select p);

System.InvalidOperationException
  HResult=0x80131509
  Message=The LINQ expression 'DbSet<Group>()
    .Join(
        inner: DbSet<GroupMember>(), 
        outerKeySelector: g1 => (object)g1.Id, 
        innerKeySelector: g2 => (object)g2.GroupId, 
        resultSelector: (g1, g2) => new TransparentIdentifier<Group, GroupMember>(
            Outer = g1, 
            Inner = g2
        ))
    .Join(
        inner: DbSet<User>(), 
        outerKeySelector: ti2 => (object)ti2.Inner.UserId, 
        innerKeySelector: u0 => (object)u0.Id, 
        resultSelector: (ti2, u0) => new TransparentIdentifier<TransparentIdentifier<Group, GroupMember>, User>(
            Outer = ti2, 
            Inner = u0
        ))
    .Where(ti3 => ti3.Inner.Email == __Email_0)
    .Where(ti3 => new ClickSelectorModel{ 
        Label = ti3.Outer.Outer.Name, 
        Object = ti3.Outer.Outer 
    }
    .Selected)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
  Source=Microsoft.EntityFrameworkCore
1

There are 1 best solutions below

2
T N On

Instead of using a condition like where ObjectCollection.Contains(objectFromQuery) I believe it would be better to use a condition like where ObjectIdList.Contains(IdFromQuery).

More specifically, if you build a Group ID list using var groupIdList = ... .Select(g => g.Id).ToList();, the subsequent LINQ query can drop the join g ... and use the condition where groupIdList.Contains(gm.GroupId).

That should translate cleanly and run server-side. You really don't want to be doing this client-side.

Try:

var groupIdList = Groups!.Where(g => g.Selected).Select(g => g.Id).ToList();
SelectedGroupPosts.AddRange(from p in DB.Post
                            join u in DB.User
                               on p.UserId equals u.Id
                            join gm in DB.GroupMember
                                on u.Id equals gm.UserId
                            where groupIdList.Contains(gm.GroupId)
                            select p);

I am assuming that the initial Groups variable is a client side collection of some sort like a UI selection list.

If you really want to do this client side (which I discourage), you can perhaps replace DB.Post with DB.Post.AsEnumerable(). That should result in an IEnumerable<> query that retrieves the entire contents of every referenced table and performs the joins and other operations client-side.