Created
March 26, 2021 07:30
-
-
Save princefishthrower/6620fcded6b2600bbd10f4100c55401c to your computer and use it in GitHub Desktop.
Dynamic Where Expressions With EF Core
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public List<AppParkingOrderModel> GetHourlyIntersectedBookingsByFacility(string facilityName) | |
{ | |
var searchCriteria = new List<Expression<Func<AppParkingOrderModel, bool>>>(); | |
var now = DateTime.Now; | |
var yearFromNow = now.AddYears(1).Year; | |
var startDateTime = new DateTime(now.Year, 1, 1, 0, 0, 0); | |
var endDateTime = new DateTime(yearFromNow, 1, 1, 0, 0, 0); | |
var hours = (endDateTime - startDateTime).TotalHours; | |
// for each hour this year, calculate and store the parking calculation model for this location | |
for (var i = 0; i < hours; i++) | |
{ | |
var dateTime = startDateTime.AddHours(i); | |
searchCriteria.Add(x => dateTime > x.StartDate && dateTime < x.EndDate); | |
} | |
// don't forget the facility criteria | |
searchCriteria.Add(x => x.FacilityName == facilityName); | |
// We're done building our search criteria - here is where we use the fancy join | |
var joinedSearchCriteria = ExpressionReplacer.Join(Expression.And, searchCriteria); | |
using (var dbContext = this.dbContextFactory.GetContext()) | |
{ | |
var query = dbContext.AppParkingOrders.AsQueryable(); | |
query = query.Where(joinedSearchCriteria); | |
return query.ToList(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
namespace YOUR_NAMESPACE_HERE | |
{ | |
public class ExpressionReplacer : ExpressionVisitor | |
{ | |
private readonly Func<Expression, Expression> replacer; | |
public ExpressionReplacer(Func<Expression, Expression> replacer) | |
{ | |
this.replacer = replacer; | |
} | |
public override Expression Visit(Expression node) | |
{ | |
return base.Visit(replacer(node)); | |
} | |
public static T ReplaceParameter<T>(T expr, ParameterExpression toReplace, ParameterExpression replacement) | |
where T : Expression | |
{ | |
var replacer = new ExpressionReplacer(e => e == toReplace ? replacement : e); | |
return (T)replacer.Visit(expr); | |
} | |
public static Expression<Func<T, TReturn>> Join<T, TReturn>(Func<Expression, Expression, BinaryExpression> joiner, IReadOnlyCollection<Expression<Func<T, TReturn>>> expressions) | |
{ | |
if (!expressions.Any()) | |
{ | |
throw new ArgumentException("No expressions were provided"); | |
} | |
var firstExpression = expressions.First(); | |
var otherExpressions = expressions.Skip(1); | |
var firstParameter = firstExpression.Parameters.Single(); | |
var otherExpressionsWithParameterReplaced = otherExpressions.Select(e => ReplaceParameter(e.Body, e.Parameters.Single(), firstParameter)); | |
var bodies = new[] { firstExpression.Body }.Concat(otherExpressionsWithParameterReplaced); | |
var joinedBodies = bodies.Aggregate(joiner); | |
return Expression.Lambda<Func<T, TReturn>>(joinedBodies, firstParameter); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment