实体框架linq orderby函数

时间:2017-08-24 07:10:41

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

我是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查询?

2 个答案:

答案 0 :(得分:1)

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

  • OrderBy按名称列出属性,希望您订购的对象具有此属性。如果您的物品没有这个属性怎么办?
  • SQL不理解GetProperty()之类的函数,因此必须在本地内存(AsEnumerable)中完成此排序,而不是使用速度更快的SQL服务器(AsQueryable)。
  • 使用存储过程按顺序排序。

通过propertyName更改参数Func<TSource, TKey> 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的结果一起转移到本地记忆中。他们只被要求你将在最终结果中使用的酒店。

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

答案 1 :(得分:0)

感谢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