Home > OS >  Can IEnumerable and IQueryable be Union?
Can IEnumerable and IQueryable be Union?

Time:07-18

I tried to Union IQueryable and IEnumerable but got this error

Union(__p_2)' 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'.

and tried many different writing methods but it doesn't work. So can IEnumerable and IQueryable be Union?

My data looks like the following:

var users = new List<Person>().AsQueryable();
var user = GetUsersByXXX(XXX, YYY);   // return IQueryable<Person>
var userExtra = GetUsersExtraByXXX(XXX, YYY); // return IEnumerable<Person>

if (userExtra.Any())
{
  users = user.Union(userExtra);
}    

return await users.AsNoTracking().OrderBy(x => x.NAME ).ToListAsync();         

class

public class Person
{
    public string CODE { get; set; }
    public string NAME { get; set; }
    public string PHONE { get; set; }
}

CodePudding user response:

You must understand those two things are two different concepts:

  • An IQueryable is an interface that "Provides functionality to evaluate queries against a specific data source"
  • An IEnumerable is an interface that "Exposes the enumerator, which supports a simple iteration over a collection".

An IQueryable can be turned into IEnumerable by calling AsEnumerable().

Note, that an IQueryable can be a non-evaluated expression. I.e. the data has not been retrieved from the database. By calling something like ToList or AsEnumerable, you enumerate, thus evaluate it. The normal (synrchonous) methods will block until that's finished. That's why there are also ToListAsync and AsAsyncEnumerable.

CodePudding user response:

The IQueryable<T> interface already implements IEnumerable<T>, you just need to cast to IEnumerable<T> to ensure you use the linq-to-objects implementation.

var users = GetUsersByXXX(XXX, YYY).AsEnumerable() // "cast" it
    .Union(GetUsersExtraByXXX(XXX, YYY));

The added check to see if the "extra" users is empty is unnecessary. If it's already empty, the union would essentially be a noop.

CodePudding user response:

The error message is caused by an error where the query you're generating cannot be translated into SQL. It would appear that IQueryable<T>.Union() cannot be translated to SQL and the error is thrown.

You would need to work around this by either calling one of the methods mentioned in the error to force the union to be performed in .NET by having Entity Framework retrieve the full set of data for GetUsersByXXX() first and then performs the union in memory.

var users = new List<T>().AsQueryable();
var user = GetUsersByXXX(XXX, YYY);   // return IQueryable<T>
var userExtra = GetUsersExtraByXXX(XXX, YYY); // return IEnumerable<T>

if (userExtra.Any())
{
  users = user.AsEnumerable().Union(userExtra);
}
  • Related