【问题标题】:Entity Framework linq orderby function实体框架 linq orderby 函数
【发布时间】:2017-08-24 07:10:41
【问题描述】:

我是 Entity Framework 和 linq 的新手。我正在使用 asp.net mvc 5 和 C#。 我写了一个动态排序的查询如下:

public static IEnumerable<T> OrderByDynamic<T>(this IEnumerable<T> source, string propertyName, bool Ascending)
{
    if (Ascending)
        return source.OrderBy(x => x.GetType().GetProperty(propertyName).GetValue(x, null));
    else
        return source.OrderByDescending(x => x.GetType().GetProperty(propertyName).GetValue(x, null));
    }

在我的存储库中我可以写:

string sortExpr = "name";
_objDB.hotels.Where(s => (s.city_id = 1))
             .OrderByDynamic(sortExpr, Ascending).ToList();

当对表的列进行排序时,此代码可以正常工作,但我需要通过 SQL 函数进行排序。我使用以下代码将函数输入到.edmx 模型中

[EdmFunction("hotelReservation.Core.Data", "getHotelMinPrice_cap")]
public static int getHotelMinPrice_cap(int Hotel_id, int capacity, DateTime enter_date, DateTime exit_date)
{
    throw new NotSupportedException("Direct calls are not supported.");
}

我的 SQL 选择类似于:

select * 
from hotel
where city_id = 1
order by dbo.getHotelMinPrice_cap(hotel.id,1,'2001-01-01', '2021-01-01')

如何在 linq 中使用动态排序编写最后一个 SQL 查询?

【问题讨论】:

  • 使用函数排序是一种不好的做法。您应该首先优化您的查询。提供你的函数getHotelMinPrice_cap,以便提供优化方式。
  • select min(room_price.price) from room_price, room_definition where room_price.roomDef_id = room_definition.ID and room_definition.Hotel_id = Hotel_id and room_definition.capacity=capacity and ( room_price.start_date between enter_date and exit_date or room_price .stop_date 在 enter_date 和 exit_date 之间)
  • 我要sql函数。
  • 该函数返回我在之前的评论中提到的 min(room_price.price)。 room_price 连接到 room_definition 并且房间定义连接到 hotel

标签: c# linq function asp.net-mvc-5 entity-framework-6


【解决方案1】:

您的解决方案引入了几个问题:

  • OrderBy一个属性的名字,希望你订购的对象有这个属性。如果你的对象没有这个属性怎么办?
  • SQL 无法理解 GetProperty() 之类的函数,因此必须在本地内存 (AsEnumerable) 中进行排序,而不是在速度更快的 SQL 服务器 (AsQueryable) 中进行。
  • 您使用存储过程来订购。

通过将参数propertyName 更改为Func&lt;TSource, TKey&gt; keySelector,可以轻松解决前两个问题:

public static IEnumerable<T, TKey> OrderByDynamic<T>(this IEnumerable<T> source, 
    Func<T, TKey> keySelector, System.ComponentModel.ListSortDirection sortDirection)
{
    if (sortDirection == ListSortDirection.Ascending)
         return source.OrderBy(keySelector);
    else
         return source.OrderByDescending(keySelector);
}

用法如下:

var result = Students.OrderByDynamis(student => student.Name, ListSortDirection.Ascending);

这种方法的优点是,如果您尝试按不存在的属性进行排序,您的编译器会报错。除此之外OrderBy可以执行AsQueryable;它可以由您的数据库而不是在本地内存中执行。

使用字符串来选择你想要的属性确实是个坏主意。

如果您输入错误,您只能在运行时检测到。此外:如果您知道在开发代码期间为 propertyName 键入什么字符串,那么您也知道要排序的对象类型,因此您可以改为编写 keySelector

您的第二个问题是将存储过程调用为排序顺序。如果您首先调用存储过程然后按返回值排序,这很容易解决:

var result = Hotels.Where(hotel => hotel...)
    .Select(hotel => new
    {
        StoredProcedureValue = CallStoredprocedure(hotel,...),
        Hotel = hotel,
    })
    .AsEnumerable()  // bring the result to local memory
    .Orderby(item => item.StoredProcedureValue)
    .Select(item => item.Hotel);

只有最终结果中的酒店与 StoredProcedures 的结果一起被传输到本地内存。仅针对您将在最终结果中使用的酒店调用它们。

但是,排序是在本地内存中完成的。如果您还希望在数据库端完成此排序,则必须创建一个新的存储过程,该过程将在执行排序之前调用其他存储过程。

【讨论】:

  • 非常感谢您的回答。我不知道你写的“StoredProcedureValue”和“CallStoredprocedure”是什么!他们是什么?我想使用一个名为“getHotelMinPrice_cap”的函数
  • 您似乎对匿名类型不熟悉。在Where 语句中,我获取Hotels 的集合,并从该集合中的每个项目(我方便地用标识符hotel 标识)创建一个匿名类的新对象,具有两个属性:StoredProcedureValue 和@ 987654339@。我不知道你如何获得存储过程的值,所以我只是调用了一个函数,它会像你通常那样做。 Orderby 将这个匿名类型 geekswithblogs.net/BlackRabbitCoder/archive/2012/06/21/… 作为输入
【解决方案2】:

感谢 Harald Coppoolse 的回答 我终于做到了:

_objDB.hotels .Select(h=> new { id= h.id, name=h.name, star=h.star,
                    minPrice1 = hotel.getHotelMinPrice_cap(h.id, 1, model.date_check_in, model.date_check_out)})
.Where(s => (s.city_id = 1))
.OrderByDynamic(sortExpr, Ascending).ToList();

在这种情况下,我可以选择sortExpr = minPrice1;,它会被sql函数排序。

我也将OrderByDynamic 函数更改为如下:

public static IQueryable<T> OrderByDynamic<T>(this IQueryable<T> q, string SortField, bool Ascending)
        {
            var param = Expression.Parameter(typeof(T), "p");
            var prop = Expression.Property(param, SortField);
            var exp = Expression.Lambda(prop, param);
            string method = Ascending ? "OrderBy" : "OrderByDescending";
            Type[] types = new Type[] { q.ElementType, exp.Body.Type };
            var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
            return q.Provider.CreateQuery<T>(mce);
        }

我在页面上找到的: https://stackoverflow.com/a/7265354/8509940

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-02
    • 1970-01-01
    • 2017-07-07
    • 2018-10-30
    • 1970-01-01
    • 2018-11-30
    • 1970-01-01
    相关资源
    最近更新 更多