【问题标题】:Linq expression with Join, and conditional OrderBy / OrderByDescending带有 Join 和条件 OrderBy / OrderByDescending 的 Linq 表达式
【发布时间】:2017-12-25 04:45:15
【问题描述】:

我正在构建一个 Linq 表达式,我没有能力在这个项目中使用 Dynamic Linq。这是我最初的声明。

                   var orderListQuery = context.Orders.Where(oExpr)
                        .Join(context.Members,
                            o => o.MemberId,
                            m => m.Id,
                            (o, m) => new {Order = o, Member = m})
                        .Where(oM => oM.Member.CustomerId == custId);

下一部分是我想有条件地添加降序表达式:

                orderListQuery = descending
                    ? orderListQuery.OrderByDescending(sortProperty)
                    : orderListQuery.OrderBy(sortProperty);

我已经看到一个帖子的错误是我需要显式列出<TSource, TKey>,但由于连接语法的复杂性,我不知道如何显式列出这些。我试过<Order, string>,还是不行。

任何帮助,谢谢。

【问题讨论】:

  • 您需要构造一个指向您的属性的表达式。但如果你只是描述你试图用这段代码完成的事情会更好,因为可能有更简单的方法来做到这一点
  • 会员ID与客户ID不同,订单上没有客户ID。但是,成员 ID 和客户 ID 是成员表的一部分。 (遗留代码问题)我正在尝试按客户 ID 获取所有订单,并根据作为视图中操作请求发送的 sortProperty 对它们进行排序。 (例如,按日期、小计等排序)。在这种情况下,sortProperty 始终是一个字符串。
  • 所以sortProperty 是一个包含属性名称的string
  • @MichaelSefranek 那你为什么不直接设置外键然后正常抓取数据
  • 从链接中获取课程,然后使用.OrderByMember("Order." + sortProperty)。降序类似。

标签: entity-framework linq linq-to-sql asp.net-mvc-5


【解决方案1】:

因为您忘记写下您要达到的目标(没有说明),所以您的问题是什么以及如何解决这个问题有点不清楚。我假设您的问题不是连接(尽管可以简化),但问题是您的方法的用户如何提供排序属性。

如何指定和使用排序属性

问题是,函数的调用者决定了排序属性。当然你只想做有用且可重用的函数,所以你不想限制你函数的用户,他可以指定任何他想要的排序属性,只要是你的join结果可以排序的东西开。

您的联接结果包含来自OrdersSequence 成员序列的数据。很明显,您的用户只能请求对来自Orders 和或Members 的值(组合)的加入结果进行排序。

Queryable.OrderBy 中的键选择器的格式为 Expression<Func<TSource, TKey>> keySelector。在这种格式中,TSource 是您加入的结果。

由于用户只能对来自Orders 和/或Members 的值进行排序,因此他的键选择器应该是一个从Orders 和/或Members 中选择键的函数

public MyResult MyQuery<TKey>(Expression<Func<Order, Member, TKey> keySelector,
    SortOrder sortOrder)
{
    ... // TODO: your query?
}

使用示例如下:

// order by Order.Id:
var result = MyQuery( (order, member) => order.Id, SortOrder.Ascending);    
// order by Member.Name:
var result = MyQuery( (order, member) => member.Name, SortOrder.Descending); 
// order by something complicated
var result = MyQuery( (order, member) => new{Id = order.Id, Name = member.Name},
             SortOrder.Ascending);

现在指定了接口,让我们定义一些帮助类并填充你的函数

class MyResult
{
    public Order Order {get; set;}
    public Member Member {get; set;}
}

class SortableMyResult<TKey>
{
    public TKey SortKey {get; set;}
    public MyResult MyResult {get; set;}
}

MyResult MyQuery<TKey>(
    Expression<Func<Order, Member, TKey> keySelector, SortOrder sortOrder)
{
    var query = context.Orders.Where(oExpr)
        .Join(context.Members,
            order => order.MemberId,
            member => member.Id,
            (order, member) => new SortableMyResult<TKey>()
            {
                SortKey = KeySelector(order, member),
                MyResult = new MyResult()
                {
                    Order = order,
                    Member = member,
                 }
            ))
            .Where(....);

我所做的是,将您的原始结果放入 MyResult 对象中。我还使用调用者提供的表达式计算了SortKey 的值,并将SortKeyMyResult 放入SortableMyResult

你有没有注意到你的Expression的返回值是一个TKey,也是属性SortKey的类型?

因为我已经定义了帮助类,所以我的编译器可以检查我没有犯任何错误。

进行排序:

IQueryable<SortableMyResult<TKey>> sortedMyResult;
switch (sortOrder)
{
    case sortOrder.Ascending:
       sortedMyResult = query.OrderBy(item => item.SortKey);
       break;
    case sortOrder.Descending:
       sortedMyResult = query.OrderByDescending(item => item.SortKey);
       break;
    default: // do not order
       sortedMyResult = query;
       break;
}

最后,提取并返回 MyResult:

return sortedMyResult.Select(sortedMyResult => sortedMyResult.MyResult);

如果你想让你的函数更加通用,你可以让调用者有机会提供一个IComparer(如果他没有,使用TKey的默认比较器):

MyResult MyQuery<TKey>(
    Expression<Func<Order, Member, TKey> keySelector,
    SortOrder sortOrder,
    IComparer<TKey> comparer = EqualityComparer<TKey>.Default)
{
    ...
}

【讨论】:

    【解决方案2】:

    感谢您提供令人难以置信的详细答案。现在已经有几个月了,关于 MVC 和现有应用程序代码的更多信息正在变得清晰。在我的例子中,我们定义了一个扩展方法,它允许 OrderBy 和 OrderByDescending 接受我们的排序模块从前端传入的字符串。例如:

        public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string propertyName)
        {
            return GetExpression(source, propertyName);
        }
    

    这会将其汇集到返回 IOrderedQueryable&lt;T&gt; 的 GetExpression 函数中

    我没有包含对此的引用,并且由于我对框架和应用程序缺乏经验,我基本上无缘无故地摇摇头。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多