我是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查询?
答案 0 :(得分:1)
您的解决方案引入了几个问题:
OrderBy
按名称列出属性,希望您订购的对象具有此属性。如果您的物品没有这个属性怎么办? 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);
}