【问题标题】:Expression OrderBy Then By returning (Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>)表达式 OrderBy Then 通过返回 (Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>)
【发布时间】:2021-03-05 11:06:21
【问题描述】:

我有一个如下的存储库类。我的问题出在 GetSelected 方法中

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{

    protected readonly DbContext Db;
    protected readonly DbSet<TEntity> DbSet;
    
    public Repository(DbContext context)
    {
        Db = context;
        DbSet = Db.Set<TEntity>();
    }

    //CRUD Operations

    public IEnumerable<TEntity> GetSelected(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, params Expression<Func<TEntity, object>>[] includeProperties)
    {
        IQueryable<TEntity> query = DbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties)
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }
}

我正在尝试通过动态提供参数来对部门进行排序,如下所示。

[Route("api/[controller]")]
[ApiController]
public class DepartmentsController : LookupBaseController<Department>
{

    private readonly IUowLookups UowLookups;

    public DepartmentsController(IUowLookups uowLookups) : base(uowLookups)
    {
        UowLookups = uowLookups;
    }

    [HttpPost] //Improvise on this
    [Route("getorderd")]
    public IEnumerable<Department> GetSelectedValues(List<string> ids)
    {
        
        var sortColumns = new Dictionary<string, Sorter>();

        sortColumns.Add("Code", new Sorter(1, SortDirection.Ascending));
        sortColumns.Add("Name", new Sorter(2, SortDirection.Ascending));

        IQueryable<Department> coll = Enumerable.Empty<Department>().AsQueryable();


        var sortExp2 = ExpressionBuilder.OrderByColumns<Department>(coll, sortColumns);

        var data = UowLookups.Repository<Department>().GetSelected(null, sortExp2 )
                                                                        
        return data.ToList();
    }
}

我有以下运行良好的代码(在 StackOverflow 中找到)。但我希望以下代码返回 (Func&lt;IQueryable&lt;TEntity&gt;, IOrderedQueryable&lt;TEntity&gt;&gt;) 而不是集合。我也不想将集合传递给这个方法,相反,它应该将TEntity 作为集合传递。可行吗?

public static IQueryable<TEntity> OrderByColumns<TEntity>(this IQueryable<TEntity> collection, IDictionary<string, Sorter> sortedColumns)
{
    // Basically sortedColumns contains the columns user wants to sort by, and 
    // the sorting direction.
    // For my screenshot, the sortedColumns looks like
    // [
    //     { "cassette", { Order = 1, Direction = SortDirection.Ascending } },
    //     { "slotNumber", { Order = 2, Direction = SortDirection.Ascending } }
    // ]

    bool firstTime = true;

    // The type that represents each row in the table
    var itemType = typeof(TEntity);

    // Name the parameter passed into the lamda "x", of the type TModel
    var parameter = Expression.Parameter(itemType, "x");

    // Loop through the sorted columns to build the expression tree
    foreach (var sortedColumn in sortedColumns.OrderBy(sc => sc.Value.Order))
    {
        // Get the property from the TModel, based on the key
        var prop = Expression.Property(parameter, sortedColumn.Key);

        // Build something like x => x.Cassette or x => x.SlotNumber
        var exp = Expression.Lambda(prop, parameter);

        // Based on the sorting direction, get the right method
        string method = String.Empty;
        if (firstTime)
        {
            method = sortedColumn.Value.Direction == SortDirection.Ascending
                ? "OrderBy"
                : "OrderByDescending";

            firstTime = false;
        }
        else
        {
            method = sortedColumn.Value.Direction == SortDirection.Ascending
                ? "ThenBy"
                : "ThenByDescending";
        }

        // itemType is the type of the TModel
        // exp.Body.Type is the type of the property. Again, for Cassette, it's
        //     a String. For SlotNumber, it's a Double.
        Type[] types = new Type[] { itemType, exp.Body.Type };

        // Build the call expression
        // It will look something like:
        //     OrderBy*(x => x.Cassette) or Order*(x => x.SlotNumber)
        //     ThenBy*(x => x.Cassette) or ThenBy*(x => x.SlotNumber)
        var mce = Expression.Call(typeof(Queryable), method, types,
            collection.Expression, exp);

        // Now you can run the expression against the collection
        collection = collection.Provider.CreateQuery<TEntity>(mce);
    }

    return collection;
}

【问题讨论】:

  • 这是一个非常奇怪的请求,感觉像是 XY 问题。你到底想达到什么目的?
  • 我有一个 Repository 类,它的 GetData 方法接受参数 Func, IOrderedQueryable> orderBy。我想将上述函数(集合)的结果作为参数 orderBy 的参数值传递给该方法(GetData)。
  • 发布使用示例。字数不够。
  • 您好 Danyliv,感谢您为我提供的帮助。我有一个 Repository 类,其方法如下
  • @Jacob,只需编辑原始问题并添加使用示例。

标签: c# linq expression-trees


【解决方案1】:

答案很简单:

public static Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> CreateOrderByFunc<TEntity>(IDictionary<string, Sorter> sortedColumns)
{
   return q => (IOrderedQueryable<TEntity>)q.OrderByColumns(sortedColumns);
}

【讨论】:

  • 您为什么要通过这些存储库使您的应用程序复杂化?对我来说只是浪费时间并使调试复杂化。
  • 谢谢Danyliv。它工作。我浪费了将近三天的时间来让这个东西工作。我可以知道一些好的资源,我可以了解表达式树、LINQ 等的详细信息吗?再次非常感谢您。关于存储库模式,据说这是最好的方法。甚至一些 MS 网站也在推荐这个。
  • @Jacob:我在这里回复你,而不是通过电子邮件。一开始我对你想要什么感到困惑。当您不知道用户想要订购哪一列时,您提到的关于动态创建表达式树 (stackoverflow.com/a/55857105/2410655) 的解决方案效果最佳。但是在您的情况下,您似乎已经知道要对哪个进行排序(代码,名称)。使用动态表达式树方法这样做是没有意义的,因为您可以将排序放在存储库返回的集合上,IQueryable
  • 谢谢你,大卫。我在上面复制的你的第一篇文章很棒。事实上,这是我经过几天的搜索后能找到的唯一一篇动态包含 OrderBy 和 ThenBy 的帖子。感谢您抽出宝贵时间回复,但我在我的垃圾邮件文件夹中找不到您的任何电子邮件。请问可以再转发一下吗?既然你写了原始代码,请问你对我的场景有什么建议
猜你喜欢
  • 2013-04-07
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多