| | 1 | | using System.Linq.Expressions; |
| | 2 | |
|
| | 3 | | namespace Fluorite.Strainer.Services.Filtering.Steps; |
| | 4 | |
|
| | 5 | | public class ApplyConsantClosureToFilterValueStep : IApplyConsantClosureToFilterValueStep |
| | 6 | | { |
| | 7 | | public void Execute(FilterExpressionWorkflowContext context) |
| | 8 | | { |
| 4 | 9 | | Guard.Against.Null(context); |
| 4 | 10 | | Guard.Against.Null(context.Term); |
| 4 | 11 | | Guard.Against.Null(context.Term.Operator); |
| 4 | 12 | | Guard.Against.Null(context.PropertyMetadata); |
| 4 | 13 | | Guard.Against.Null(context.PropertyMetadata.PropertyInfo); |
| 4 | 14 | | Guard.Against.Null(context.FilterTermConstant); |
| | 15 | |
|
| 4 | 16 | | var constantClosureType = context.Term.Operator.IsStringBased |
| 4 | 17 | | ? typeof(string) |
| 4 | 18 | | : context.PropertyMetadata.PropertyInfo.PropertyType; |
| | 19 | |
|
| 4 | 20 | | if (context.FilterTermConstant.GetType() != constantClosureType) |
| | 21 | | { |
| 2 | 22 | | throw new InvalidOperationException( |
| 2 | 23 | | "Cannot get a closure over constant using wrong type. " + |
| 2 | 24 | | $"Give value is of {context.FilterTermConstant.GetType().Name} type while expected target type " + |
| 2 | 25 | | $"is of {constantClosureType.Name} type."); |
| | 26 | | } |
| | 27 | |
|
| 2 | 28 | | context.FinalExpression = GetClosureOverConstant( |
| 2 | 29 | | context.FilterTermConstant, |
| 2 | 30 | | constantClosureType); |
| 2 | 31 | | } |
| | 32 | |
|
| | 33 | | // Workaround to ensure that the filter value gets passed as a parameter in generated SQL from EF Core |
| | 34 | | // See https://github.com/aspnet/EntityFrameworkCore/issues/3361 |
| | 35 | | // Expression.Constant passed the target type to allow Nullable comparison |
| | 36 | | // See http://bradwilson.typepad.com/blog/2008/07/creating-nullab.html |
| | 37 | | private Expression GetClosureOverConstant<T>(T constant, Type targetType) |
| | 38 | | { |
| 2 | 39 | | return Expression.Constant(constant, targetType); |
| | 40 | | } |
| | 41 | | } |