Վերջերս ես միացա մի նախագծի, որտեղ Sort
մեթոդը պայմանականորեն անցնում էր լամբդա արտահայտությունով LINQ հարցմանը, որպեսզի որոշի, թե որ հատկությունը պետք է տեսակավորվի: Խնդիրն այն էր, որ lambda արտահայտությունը փոխանցվում էր Func<TEntity, Object
>-ով և ոչ թե Expression<Func<TEntity, Object>>
-ով, այնպես որ տեսակավորումը տեղի էր ունենում հիշողության մեջ և ոչ թե տվյալների բազայում (որովհետև OrderBy
-ի գերբեռնվածությունը, որը վերցնում է IEnumerable
կոչվում է): Սա SortWithDelegate
տարբերակն է (տես ստորև):
Երբ ես օգտագործում եմ Expression<Func<TEntity, Object>>
-ը (տես SortWithExpression
ստորև), այնուհետև, երբ տողի հատկությունը փոխանցվում է orderBy
պարամետրում, պատվերը ճիշտ է կատարվում տվյալների բազայում: Այնուամենայնիվ, երբ ես փորձում եմ տեսակավորել ամբողջ թվով (կամ ամսաթիվը) օգտագործելով Expression<Func<TEntity, Object>>
, ես ստանում եմ հետևյալ սխալը.
Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
Սրանից խուսափելու համար ես պետք է փաթաթեմ ամբողջ թվի կամ ամսաթվերի դաշտը, որը պետք է տեսակավորվի անանուն տիպի ներսում, ինչպես օրինակ՝ orderByFunc = sl => new {sl.ParentUnit.Id};
: Ես հասկանում եմ, որ պետք է դա անեմ, քանի որ Func
-ի վերադարձի տեսակը Object
է: Այնուամենայնիվ, այն, ինչ ես չեմ հասկանում, այն է, թե ինչու ես պետք է դա անեմ, երբ աշխատում եմ LINQ to Entities մատակարարի հետ, բայց ոչ LINQ to Objects մատակարարի հետ:
void Main()
{
var _context = new MyContext();
string sortProperty = "Id";
bool sortAscending = false;
IQueryable<Qualification> qualifications = _context.Qualifications.Include(q => q.ParentUnit);
qualifications = SortWithExpression(sortProperty, sortAscending, qualifications);
qualifications.Dump();
}
private static IQueryable<Qualification> SortWithDelegate(string orderBy, bool sortAscending, IQueryable<Qualification> qualificationsQuery)
{
Func<Qualification, Object> orderByFunc;
switch (orderBy)
{
case "Name":
orderByFunc = sl => sl.Name;
break;
case "ParentUnit":
orderByFunc = sl => sl.ParentUnit.Name;
break;
case "Id":
orderByFunc = sl => sl.ParentUnit.Id;
break;
case "Created":
orderByFunc = sl => sl.Created;
break;
default:
orderByFunc = sl => sl.Name;
break;
}
qualificationsQuery = sortAscending
? qualificationsQuery.OrderBy(orderByFunc).AsQueryable()
: qualificationsQuery.OrderByDescending(orderByFunc).AsQueryable();
return qualificationsQuery;
}
private static IQueryable<Qualification> SortWithExpression(string orderBy, bool sortAscending, IQueryable<Qualification> qualificationsQuery)
{
Expression<Func<Qualification, Object>> orderByFunc;
switch (orderBy)
{
case "Name":
orderByFunc = sl => sl.Name;
break;
case "ParentUnit":
orderByFunc = sl => sl.ParentUnit.Name;
break;
case "Id":
orderByFunc = sl => new {sl.ParentUnit.Id};
break;
case "Created":
orderByFunc = sl => new {sl.Created};
break;
default:
orderByFunc = sl => sl.Name;
break;
}
qualificationsQuery = sortAscending
? qualificationsQuery.OrderBy(orderByFunc)
: qualificationsQuery.OrderByDescending(orderByFunc);
return qualificationsQuery;
}
Ավելացված է
Պարզապես մտածեցի, որ այս խնդրին կավելացնեմ իմ սեփական լուծումը: int
և datetime
բռնցքամարտից խուսափելու համար ես IQueryable<T>
-ի վրա ստեղծել եմ ընդհանուր ընդլայնման մեթոդ, որին անցնում եմ լամբդա արտահայտությունով՝ ցույց տալու տեսակավորման դաշտը և բուլյան՝ նշելով, թե արդյոք տեսակավորման կարգը պետք է լինի աճողական, թե ոչ:
public static IQueryable<TSource> OrderBy<TSource, TResult>(this IQueryable<TSource> query, Expression<Func<TSource, TResult>> func, bool sortAscending)
{
return sortAscending ?
query.OrderBy(func) :
query.OrderByDescending(func);
}
private static IQueryable<Qualification> Sort(string orderBy, bool sortAscending, IQueryable<Qualification> qualificationsQuery)
{
switch (orderBy)
{
case "Name":
return qualificationsQuery.OrderBy(sl => sl.Name, sortAscending);
case "ParentUnit":
return qualificationsQuery.OrderBy(s1 => s1.ParentUnit.Name, sortAscending);
default:
return qualificationsQuery.OrderBy(sl => sl.Name, sortAscending);
}
}