我下面有一个很大的代码块,实际的代码并不重要,但是为了证明我尝试做的事情而包含在内。
我有一个公共静态表达式> GetFreeSpotCount(...)
此方法返回一个表达式,我想在另一个方法中重用
表达式本身返回一个数字,表示符合某些标准的[model]列表中[model]的数量。
我希望新方法返回上面的数字大于0的所有[模型]。
下面是我目前的代码,我想避免重复这么多代码
public static Expression<Func<ExamTimeSlot, int>> GetFreeSpotCountFor(List<Guid> drivingSchoolIds)
{
return ets =>
ets.Participants
- ets.Exams.Where(ex => ex.Status == ExamStatus.Pending).Count(e => !ets.ExamTimeSlotReservations.Any(r => r.DrivingSchoolId == e.BookedByDrivingSchoolId))
- ((int?)ets.ExamTimeSlotReservations.Sum(r => (ets.Exams.Where(ex => ex.Status == ExamStatus.Pending).Where(e => e.BookedByDrivingSchoolId == r.DrivingSchoolId).Count()
- r.ReservedSpots) > 0 ? (ets.Exams.Where(ex => ex.Status == ExamStatus.Pending).Where(e => e.BookedByDrivingSchoolId == r.DrivingSchoolId).Count() - r.ReservedSpots) : 0) ?? 0)
- ((int?)ets.ExamTimeSlotReservations.Sum(r => r.ReservedSpots) ?? 0)
+ ((((int?)ets.ExamTimeSlotReservations.Where(r => drivingSchoolIds.Any(id => r.DrivingSchoolId == id)).Sum(r => r.ReservedSpots) ?? 0)
- ets.Exams.Where(ex => drivingSchoolIds.Any(id => ex.BookedByDrivingSchoolId == id) && ex.Status == ExamStatus.Pending).Count()) >= 0 ?
(((int?)ets.ExamTimeSlotReservations.Where(r => drivingSchoolIds.Any(id => r.DrivingSchoolId == id)).Sum(r => r.ReservedSpots) ?? 0)
- ets.Exams.Where(ex => drivingSchoolIds.Any(id => ex.BookedByDrivingSchoolId == id) && ex.Status == ExamStatus.Pending).Count())
: 0);
}
和
public static Expression<Func<ExamTimeSlot, bool>> GetExamTimeSlotsWithFreeSpotsFor(List<Guid> drivingSchoolIds)
{
return ets =>
(ets.Participants
- ets.Exams.Where(ex => ex.Status == ExamStatus.Pending).Count(e => !ets.ExamTimeSlotReservations.Any(r => r.DrivingSchoolId == e.BookedByDrivingSchoolId))
- ((int?)ets.ExamTimeSlotReservations.Sum(r => (ets.Exams.Where(ex => ex.Status == ExamStatus.Pending).Where(e => e.BookedByDrivingSchoolId == r.DrivingSchoolId).Count()
- r.ReservedSpots) > 0 ? (ets.Exams.Where(ex => ex.Status == ExamStatus.Pending).Where(e => e.BookedByDrivingSchoolId == r.DrivingSchoolId).Count() - r.ReservedSpots) : 0) ?? 0)
- ((int?)ets.ExamTimeSlotReservations.Sum(r => r.ReservedSpots) ?? 0)
+ ((((int?)ets.ExamTimeSlotReservations.Where(r => drivingSchoolIds.Any(id => r.DrivingSchoolId == id)).Sum(r => r.ReservedSpots) ?? 0)
- ets.Exams.Where(ex => drivingSchoolIds.Any(id => ex.BookedByDrivingSchoolId == id) && ex.Status == ExamStatus.Pending).Count()) >= 0 ?
(((int?)ets.ExamTimeSlotReservations.Where(r => drivingSchoolIds.Any(id => r.DrivingSchoolId == id)).Sum(r => r.ReservedSpots) ?? 0)
- ets.Exams.Where(ex => drivingSchoolIds.Any(id => ex.BookedByDrivingSchoolId == id) && ex.Status == ExamStatus.Pending).Count())
: 0)) > 0;
}
我想做类似的事情:
public static Expression<Func<ExamTimeSlot, bool>> GetExamTimeSlotsWithFreeSpotsFor(List<Guid> drivingSchoolIds)
{
return ets => GetFreeSpotCountFor(drivingSchoolIds) > 0;
}
我已尝试将Expression.GreaterThan与第一个表达式一起使用,但由于我需要结果AND [model],我无法找到使其工作的方法。
答案 0 :(得分:1)
实际上没有更简单的方法(没有第三方库)。似乎我们应该能够轻松地完成你所写的内容,但是由于我们在创建表达式树时依赖的语法糖,我们会误导。
public static Expression<Func<ExamTimeSlot, bool>> GetExamTimeSlotsWithFreeSpotsFor(List<Guid> drivingSchoolIds)
{
var param = Expression.Parameter(typeof(ExamTimeSlot));
//We want to create a new lambda which will invoke `GetFreeSpotCountFor` with our parameter, and then check it's greater than 0
var newBody =
Expression.GreaterThan(
//View this as GetFreeSpotCountFor(drivingSchoolIds)(param) - where param will be given to us when this lambda is invoked
Expression.Invoke(
GetFreeSpotCountFor(drivingSchoolIds),
param
),
//Pass the right-hand value (0) to the GreaterThan check
Expression.Constant(0)
);
var lambda = Expression.Lambda<Func<ExamTimeSlot, bool>>(newBody, param);
return lambda;
}
一旦我们得到Expression
对象,我们需要手动构建表达式树。
上面将你的lambda包装在一个新的lambda中,该lambda调用并将结果与0进行比较。另一种方法是检查现有的lambda .Body
,并将.GreaterThan
附加到它,然后返回一个全新的lambda。
即:
public static Expression<Func<ExamTimeSlot, bool>> GetExamTimeSlotsWithFreeSpotsFor(List<Guid> drivingSchoolIds)
{
//This grabs the existing lambda, which we will work on
var oldLambda = GetFreeSpotCountFor(drivingSchoolIds);
var newBody =
//Invoke `GreaterThan` directly on the old lambda's Body
Expression.GreaterThan(
oldLambda.Body,
//Pass the right-hand value (0) to the GreaterThan check
Expression.Constant(0)
);
//Now, we need to pass in the old parameters, and build a new lambda.
var lambda = Expression.Lambda<Func<ExamTimeSlot, bool>>(newBody, oldLambda.Parameters);
return lambda;
}
答案 1 :(得分:0)
为什么你特意想要组合表达式而不是组合查询调用?一种简单的方法是让你的GetExamTimeSlotsWithFreeSpots不接受并返回一个表达式但是接受并返回一个查询,并在该查询中添加两个where子句(每个子句都是你存储的表达式)。
public static IQueryable<ExamTimeSlot> WhereExamTimeSlotsWithFreeSpotsFor (IQueryable<ExamTimeSlot> Source, List<Guid> drivingSchoolIds)
{
var FirstFilter = GetFreeSpotCountFor(drivingSchoolIds);
var SecondFilter = GetExamTimeSlotsWithFreeSpotsFor(drivingSchoolIds);
return Source
.Where(FirstFilter)
.Where(SecondFilter);
}
这样你就不需要组合任何东西,让你的LINQ提供者自己组合两个where子句。
请注意,如果你真的想手动组合表达式,那么就可以了,但据我所知,它需要第三方API或直接使用表达式树(不仅仅是享受编译器魔术为你做的事)。如果它只是返回一个带有2个表达式的表达式并且在它们之间抛出一个和运算符,但这对于相同的结果来说似乎有点过分并不难。