最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Create an IOrderedQueryable and use it on another List - Stack Overflow

programmeradmin1浏览0评论

I create an IOrderedQueryable and want to use the query on different lists later in the process. But all I get are empty results.

My code for creating the query:

IOrderedQueryable<SomeElement> sortingQuery;

if (userInputA)
{
    sortingQuery = new List<SomeElement>().AsQueryable()
        .OrderBy(clt => clt.PropertyA);
}
else
{
    sortingQuery = new List<SomeElement>().AsQueryable()
        .OrderBy(clt => cltPropertyB);
}

sortingQuery = userInputB 
    ? sortingQuery.ThenBy(clt => clt.PropertyC ?? int.MaxValue) 
    : sortingQuery.ThenByDescending(clt => clt.PropertyD ?? int.MaxValue);

sortingQuery = sortingQuery.ThenBy(clt => clt.PropertyZ);

Later I want to execute the query on n lists:

while(process)
{
    List<SomeElement> data = await GetNewData();

    var orderedData = data.AsQueryable()
           .Provider
           .CreateQuery<SomeElement>(sortingQuery.Expression)
           .ToList();

    //...
}

Unfortunately, the orderedData list is always empty, regardless of the data.Count().

What am I doing wrong?

I create an IOrderedQueryable and want to use the query on different lists later in the process. But all I get are empty results.

My code for creating the query:

IOrderedQueryable<SomeElement> sortingQuery;

if (userInputA)
{
    sortingQuery = new List<SomeElement>().AsQueryable()
        .OrderBy(clt => clt.PropertyA);
}
else
{
    sortingQuery = new List<SomeElement>().AsQueryable()
        .OrderBy(clt => cltPropertyB);
}

sortingQuery = userInputB 
    ? sortingQuery.ThenBy(clt => clt.PropertyC ?? int.MaxValue) 
    : sortingQuery.ThenByDescending(clt => clt.PropertyD ?? int.MaxValue);

sortingQuery = sortingQuery.ThenBy(clt => clt.PropertyZ);

Later I want to execute the query on n lists:

while(process)
{
    List<SomeElement> data = await GetNewData();

    var orderedData = data.AsQueryable()
           .Provider
           .CreateQuery<SomeElement>(sortingQuery.Expression)
           .ToList();

    //...
}

Unfortunately, the orderedData list is always empty, regardless of the data.Count().

What am I doing wrong?

Share Improve this question edited Jan 29 at 17:46 marc_s 757k184 gold badges1.4k silver badges1.5k bronze badges asked Jan 29 at 14:23 HWS-SLSHWS-SLS 1668 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 2

You don't need to use an empty list to create a queryable. You can just apply the queryable expression using a function. Also lists aren't really IQueryable, you may as well return IEnumerable

IOrderedEnumerable<SomeElement> GetSortedQuery(IEnumerable<SomeElement> list, bool userInputA, bool userInputB)
{
    var sortingQuery = userInputA
        ? list.OrderBy(clt => clt.PropertyA)
        : list.OrderBy(clt => cltPropertyB);

    sortingQuery = userInputB 
        ? sortingQuery.ThenBy(clt => clt.PropertyC ?? int.MaxValue) 
        : sortingQuery.ThenByDescending(clt => clt.PropertyD ?? int.MaxValue);

    sortingQuery = sortingQuery.ThenBy(clt => clt.PropertyZ);
    return sortingQuery;
}

Then just do

while (process)
{
    List<SomeElement> data = await GetNewData();
    var orderedData = GetSortedQuery(data, userInputA, userInputB);
    //...
}

I would encapsulate everything into helper classes to simplify usage. However, I do not recommend using EnumerableQuery if performance is a concern.

Updated Code Using New Extension Methods:

var sortingQuery = Enumerable.Empty<SomeElement>().CreateTemplateQuery();

if (userInputA)
    sortingQuery = sortingQuery.OrderBy(clt => clt.PropertyA);
else
    sortingQuery = sortingQuery.OrderBy(clt => clt.PropertyB);

sortingQuery = userInputB
    ? sortingQuery.ThenBy(clt => clt.PropertyC ?? int.MaxValue)
    : sortingQuery.ThenByDescending(clt => clt.PropertyD ?? int.MaxValue);

sortingQuery = sortingQuery.ThenBy(clt => clt.PropertyZ);

Reusing the Sorting Logic Later:

while (process)
{
    List<SomeElement> data = await GetNewData();

    var orderedData = data.ApplyOrderTemplate(sortingQuery)
       .ToList();

    // Further processing...
}

Implementation of Helper Classes:

public static class OrderingHelper
{
    static readonly IQueryProvider EnumerableProvider = Array.Empty<int>().AsQueryable().Provider;

    static class OrderHelperImpl<T>
    {
        // A placeholder that can be easily replaced
        public static ParameterExpression Anchor = Expression.Parameter(typeof(IQueryable<T>), "anchor");

        public static IOrderedQueryable<T> CreateTemplateQuery()
        {
            return (IOrderedQueryable<T>)EnumerableProvider.CreateQuery<T>(Anchor);
        }
    }

    public static IOrderedQueryable<T> CreateTemplateQuery<T>(this IEnumerable<T> enumerable)
    {
        return OrderHelperImpl<T>.CreateTemplateQuery();
    }

    public static IOrderedQueryable<T> ApplyOrderTemplate<T>(this IEnumerable<T> source, IQueryable<T> template)
    {
        var visitor = new ReplacingVisitor(OrderHelperImpl<T>.Anchor, source.AsQueryable().Expression);
        var newExpression = visitor.Visit(template.Expression);
        return (IOrderedQueryable<T>)EnumerableProvider.CreateQuery<T>(newExpression);
    }

    class ReplacingVisitor : ExpressionVisitor
    {
        private readonly Expression _original;
        private readonly Expression _replacement;

        public ReplacingVisitor(Expression original, Expression replacement)
        {
            _original = original;
            _replacement = replacement;
        }

        [return: NotNullIfNotNull(nameof(node))]
        public override Expression? Visit(Expression? node)
        {
            return node == _original ? _replacement : base.Visit(node);
        }
    }
}

Why This Approach Is Inefficient:

This method is not ideal for performance because EnumerableProvider dynamically generates a compiled delegate for each enumeration, leading to unnecessary overhead.

发布评论

评论列表(0)

  1. 暂无评论