带有Join的Linq表达式,以及条件OrderBy / OrderByDescending

时间:2017-07-19 18:37:42

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

我正在构建一个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>,但无效。

一切都有帮助,谢谢。

2 个答案:

答案 0 :(得分:1)

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

如何指定和使用排序属性

问题是,函数的调用者决定了sort属性。当然你只想制作有用且非常可重复使用的函数,所以你不想限制函数的用户,他可以指定他想要的任何排序属性,只要它是你的连接的结果可以被排序的东西上。

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

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

由于用户只能对来自Orders和/或Members的值进行排序,因此其密钥选择器应该是从Orders和/或{{1}中选择密钥的函数}

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的值,并将SortKeySortKey放在MyResult中。

您是否注意到Expression的返回值是TKey,它也是SortKey属性的类型?

因为我已经定义了辅助类,所以我的编译器可以检查我没有出错。

进行排序:

SortableMyResult

最后,提取并返回MyResult:

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;
}

如果你想让你的函数更通用,你可以让调用者提供return sortedMyResult.Select(sortedMyResult => sortedMyResult.MyResult); 的机会(如果他没有,则使用IComparer的默认比较器):

TKey

答案 1 :(得分:0)

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

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

然后,它会将其汇总到一个返回IOrderedQueryable<T>

的GetExpression函数中

我没有包含对此的引用,由于我对框架和应用程序的经验不足,我基本上没有理由摇头。