Home > OS >  The functions .all() or .any() from LinQ crashes when using it on an array of long
The functions .all() or .any() from LinQ crashes when using it on an array of long

Time:10-19

I'm currently creating an API for my database which contains Users and Sites. They have a many-to-many relation. When I did my "Get" function, I wanted it to be the most generic possible : It can have a random number of parameters and even an "order by" parameter. One of the parameters is ofcourse the sites of the users which is a long[] of Ids. I want that ALL of my ids in my array corresponds in the id of the Sites in my User.

Ex : List[1,3] => If my User have Sites 1,2,3 is ok but if he has 1,2,4 it's not ok.

public async Task<ActionResult<IEnumerable<User>>> GetUser(IEnumerable <long> idSites){
...
resultUsers = DBContext.Users.Where(x => (idSites.Count() > 0)
                                                    ? (x.Sites.Count() > 0)
                                                        ? idSites.All(id => x.Sites.All(site => site.Id == id))
                                                        : false
                                                    : true).ToList();
...
}

For a sake of clarity I only put this parameter in the where() but normally all my parameters are in the where(). The first ternary expression is to check if if I want to filter on this parameter. The second one is to avoid an empty list of sites counting as "ok" when it's not (i don't know why it was working but whatever).

The first .all() never gave me any problems. The second one crashes in my opinion whether I put all() or any().

I'm going to put the error BUT I know it won't help. The error says I can't assign my line to my list because it misses the "ToList()" function. It's because it is crashing mid lambda expression and never gets to the "toList()" (in my opinion).

EDIT : When I put "AsEnumerable" the error is removed, but my returned list is always empty (when i put don't filter on that parameter it's full). And more than that, I can't use the .include() functions when I use this tag, which I use to get the value of the Sites.

There it is :

System.InvalidOperationException: The LINQ expression 'DbSet<User>()
    .Where(u => DbSet<Dictionary<string, object>>("SiteUser")
        .Where(s => EF.Property<Nullable<long>>(u, "Id") != null && object.Equals(
            objA: (object)EF.Property<Nullable<long>>(u, "Id"), 
            objB: (object)EF.Property<Nullable<long>>(s, "UsersId")))
        .Join(
            inner: DbSet<Site>(), 
            outerKeySelector: s => EF.Property<Nullable<long>>(s, "SitesId"), 
            innerKeySelector: s0 => EF.Property<Nullable<long>>(s0, "Id"), 
            resultSelector: (s, s0) => new TransparentIdentifier<Dictionary<string, object>, Site>(
                Outer = s, 
                Inner = s0
            ))
        .Count() > 0 ? __complexUserParameters_idSites_0
        .All(id => DbSet<Dictionary<string, object>>("SiteUser")
            .Where(s1 => EF.Property<Nullable<long>>(u, "Id") != null && object.Equals(
                objA: (object)EF.Property<Nullable<long>>(u, "Id"), 
                objB: (object)EF.Property<Nullable<long>>(s1, "UsersId")))
            .Join(
                inner: DbSet<Site>(), 
                outerKeySelector: s1 => EF.Property<Nullable<long>>(s1, "SitesId"), 
                innerKeySelector: s2 => EF.Property<Nullable<long>>(s2, "Id"), 
                resultSelector: (s1, s2) => new TransparentIdentifier<Dictionary<string, object>, Site>(
                    Outer = s1, 
                    Inner = s2
                ))
            .All(ti0 => ti0.Inner.Id == id) == True) : False)' 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.

If you need any more information to help me let me know. Thank you a lot.

CodePudding user response:

Maybe you could Check Intersect() (should be fine on int-s)?

 public async Task<ActionResult<IEnumerable<User>>> GetUser(IEnumerable <long> idSites){
 if (idSites == null || !idSites.Any())
 {
   resultUsers = DBContext.Users.Tolist(); //Why checking the Count of the input parameter on every User? 
 }
 else
 {
   resultUsers = DBContext.Users.Where(x => x.Sites.Count() > 0 ?
                                         idSites.Intersect( x.Sites).Count() == idSites.Count() :                                        
                                          false)
                                .ToList();
  }
 ...
}

CodePudding user response:

Try to use Count instead of All. It should work:

var query = context.Users.AsQueryable();
if (sites?.Count > 0)
{
    query = query.Where(x => x.Sites.Count(s => sites.Contains(s.Id)) == sites.Count);
}

return await query.ToArrayAsync(cancellationToken);

HttpContext.RequestAborted can be used as cancellationToken. Of course you could add .Include(x => x.Sites).

If sites variable is array Count must be changed on Length.

  • Related