C# 实体框架linq orderby函数

C# 实体框架linq orderby函数,c#,linq,function,asp.net-mvc-5,entity-framework-6,C#,Linq,Function,Asp.net Mvc 5,Entity Framework 6,我不熟悉实体框架和linq。我正在使用asp.NETMVC5和C。 我编写了一个具有动态排序的查询,如下所示: public static IEnumerable<T> OrderByDynamic<T>(this IEnumerable<T> source, string propertyName, bool Ascending) { if (Ascending) return source.OrderBy(x => x.Get

我不熟悉实体框架和linq。我正在使用asp.NETMVC5和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));
    }
当排序在表的列上时,这段代码可以正常工作,但我需要使用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查询?

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

您可以按名称按属性排序,希望所排序的对象具有此属性。如果您的对象没有此属性怎么办? SQL不理解像GetProperty这样的函数,所以这种排序必须在本地内存AsEnumerable中完成,而不是在更快的SQL server AsQueryable中完成。 您可以使用存储过程进行排序。 前两个问题可以通过Func keySelector更改参数propertyName轻松解决:

这种方法的优点是,如果您试图按不存在的属性排序,编译器会抱怨。除此之外,此OrderBy还可以作为可查询项执行;它可以由数据库而不是在本地内存中执行

使用字符串来选择所需的属性确实不是一个好主意

如果您输入错误,您只能在运行时检测到。此外:如果您知道在代码开发期间为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);
只有最终结果中的酒店才会与StoredProcess的结果一起传输到本地内存。他们只为你在最终结果中使用的酒店打过电话


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

感谢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);
        }
我在第页找到:

使用函数进行排序是一种不好的做法。您应该首先优化查询。提供您的函数getHotelMinPrice\u cap,以便提供优化方式。从room\u price中选择minroom\u price.price,room_definition其中room_price.roomDef_id=room_definition.id和room_definition.Hotel_id=Hotel_id和room_definition.capacity=容量和room_price.start_date介于输入日期和退出日期之间或room_price.stop_date介于输入日期和退出日期之间我请求的sql函数。该函数返回我指定的minroom_price.price在之前的评论中提到。房间价格已加入房间定义,房间定义已加入酒店。感谢您的回答。我不知道你写的“StoredProcedureValue”和“CallStoredprocedure”是什么!它们是什么??我想使用一个名为“getHotelMinPrice_cap”的函数,因为您似乎不熟悉匿名类型。在Where语句中,我获取了Hotels集合,并从该集合中的每个项目(我方便地用标识符hotel标识)创建了一个匿名类的新对象,该对象有两个属性:StoredProcedureValue和hotel。我不知道如何获得存储过程的值,所以我只调用了一个函数,该函数将按照通常的方式执行。Orderby将此匿名类型作为输入
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);
_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();
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);
        }