Jonathan Wood Jonathan Wood - 3 months ago 47
C# Question

The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'

I have the following query.

var query = Repository.Query<Product>()
.Where(p => !p.IsDeleted && p.Article.ArticleSections.Count() > 0)
.Select(p => new
{
OfficeId = p.TariffCategory.Office.Id,
Office = p.TariffCategory.Office.Name,
Category = p.TariffCategory.Description,
ArticleId = p.Article.Id,
Article = p.Article.Title,
Destinations = p.ProductDestinations.OrderBy(pd => pd.Destination.Description).Select(pd => new { Id = pd.DestinationId, Name = pd.Destination.Description }),
GlobalDestinations = p.AllDestinationsInOffice,
p.Article.LastReviewedDate,
p.Article.CreatedDate,
p.Article.CreatedByEmployee
});
query = query.Concat(Repository.Query<Package>()
.Where(pkg => !pkg.IsDeleted && pkg.Article.ArticleSections.Count() > 0)
.Select(pkg => new
{
OfficeId = pkg.TariffCategory.Office.Id,
Office = pkg.TariffCategory.Office.Name,
Category = pkg.TariffCategory.Description,
ArticleId = pkg.Article.Id,
Article = pkg.Article.Title,
Destinations = pkg.PackageDestinations.OrderBy(pd => pd.Destination.Description).Select(pd => new { Id = pd.DestinationId, Name = pd.Destination.Description }),
GlobalDestinations = pkg.AllDestinationsInOffice,
pkg.Article.LastReviewedDate,
pkg.Article.CreatedDate,
pkg.Article.CreatedByEmployee
}));
query = query.Concat(Repository.Query<Backgrounder>()
.Where(bkgd => !bkgd.IsDeleted && bkgd.Article.ArticleSections.Count() > 0)
.Select(bkgd => new
{
OfficeId = bkgd.TariffCategory.Office.Id,
Office = bkgd.TariffCategory.Office.Name,
Category = bkgd.TariffCategory.Description,
ArticleId = bkgd.Article.Id,
Article = bkgd.Article.Title,
Destinations = bkgd.BackgrounderDestinations.OrderBy(bd => bd.Destination.Description).Select(bd => new { Id = bd.DestinationId, Name = bd.Destination.Description }),
GlobalDestinations = bkgd.AllDestinationsInOffice,
bkgd.Article.LastReviewedDate,
bkgd.Article.CreatedDate,
bkgd.Article.CreatedByEmployee
}));

// Apply filters
if (OfficeIds.Any())
query = query.Where(a => OfficeIds.Contains(a.OfficeId));
if (DestinationIds.Any())
query = query.Where(a => a.GlobalDestinations || a.Destinations.Any(d => DestinationIds.Contains(d.Id)));
if (!string.IsNullOrEmpty(ArticleTitle))
query = query.Where(a => a.Article.Contains(ArticleTitle));
if (!string.IsNullOrEmpty(TariffCategory))
query = query.Where(a => a.Category.Contains(TariffCategory));

// Sort results
query = query.OrderBy(a=> a.Office).ThenBy(a => a.Category).ThenBy(a => a.Article);

var articles = query.ToList();


However, when I run this query I get an exception.


The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'


The query searches through articles in my database. Because articles can be related to a
Product
,
Package
or
Backgrounder
, and I need information from the related table, I concatenate separate queries for each of those items.

I've narrowed it down to the assignment to
Destinatons
. Apparently, this constitute a query within the query associated with
Concat()
. (If I remove the second two queries and the associated
Concat()
calls, it works fine.)

After looking at this a while, I'm having trouble seeing another way to construct my query without making it much, much slower.

Does anyone see any tricks I might have missed to work around the exception?

Answer

Unfortunately there are no tricks. The only reasonable way I see to make this work without totally rewriting it is to execute the queries separately (applying all possible filters) and then do Concat and ordering in memory like this:

var queries = new [] 
{
    Repository.Query<Product>()
    .Where(p => !p.IsDeleted && p.Article.ArticleSections.Count() > 0)
    .Select(p => new
    {
        OfficeId = p.TariffCategory.Office.Id,
        Office = p.TariffCategory.Office.Name,
        Category = p.TariffCategory.Description,
        ArticleId = p.Article.Id,
        Article = p.Article.Title,
        Destinations = p.ProductDestinations.OrderBy(pd => pd.Destination.Description).Select(pd => new { Id = pd.DestinationId, Name = pd.Destination.Description }),
        GlobalDestinations = p.AllDestinationsInOffice,
        p.Article.LastReviewedDate,
        p.Article.CreatedDate,
        p.Article.CreatedByEmployee
    }),

    Repository.Query<Package>()
    .Where(pkg => !pkg.IsDeleted && pkg.Article.ArticleSections.Count() > 0)
    .Select(pkg => new
    {
        OfficeId = pkg.TariffCategory.Office.Id,
        Office = pkg.TariffCategory.Office.Name,
        Category = pkg.TariffCategory.Description,
        ArticleId = pkg.Article.Id,
        Article = pkg.Article.Title,
        Destinations = pkg.PackageDestinations.OrderBy(pd => pd.Destination.Description).Select(pd => new { Id = pd.DestinationId, Name = pd.Destination.Description }),
        GlobalDestinations = pkg.AllDestinationsInOffice,
        pkg.Article.LastReviewedDate,
        pkg.Article.CreatedDate,
        pkg.Article.CreatedByEmployee
    }),

    Repository.Query<Backgrounder>()
    .Where(bkgd => !bkgd.IsDeleted && bkgd.Article.ArticleSections.Count() > 0)
    .Select(bkgd => new
    {
        OfficeId = bkgd.TariffCategory.Office.Id,
        Office = bkgd.TariffCategory.Office.Name,
        Category = bkgd.TariffCategory.Description,
        ArticleId = bkgd.Article.Id,
        Article = bkgd.Article.Title,
        Destinations = bkgd.BackgrounderDestinations.OrderBy(bd => bd.Destination.Description).Select(bd => new { Id = bd.DestinationId, Name = bd.Destination.Description }),
        GlobalDestinations = bkgd.AllDestinationsInOffice,
        bkgd.Article.LastReviewedDate,
        bkgd.Article.CreatedDate,
        bkgd.Article.CreatedByEmployee
    }),
};

// Apply filters
if (OfficeIds.Any())
    for (int i = 0; i < queries.Length; i++) queries[i] = queries[i].Where(a => OfficeIds.Contains(a.OfficeId));
if (DestinationIds.Any())
    for (int i = 0; i < queries.Length; i++) queries[i] = queries[i].Where(a => a.GlobalDestinations || a.Destinations.Any(d => DestinationIds.Contains(d.Id)));
if (!string.IsNullOrEmpty(ArticleTitle))
    for (int i = 0; i < queries.Length; i++) queries[i] = queries[i].Where(a => a.Article.Contains(ArticleTitle));
if (!string.IsNullOrEmpty(TariffCategory))
    for (int i = 0; i < queries.Length; i++) queries[i] = queries[i].Where(a => a.Category.Contains(TariffCategory));

// Switch to LINQ to Objects and concatenate the results
var result = queries.Select(query => query.AsEnumerable()).Aggregate(Enumerable.Concat);

// Sort results
result = result.OrderBy(a=> a.Office).ThenBy(a => a.Category).ThenBy(a => a.Article);

var articles = result.ToList();
Comments