Create an expression tree that generates a parametric query for the Entity Framework

I am trying to create a generic class that will be used to compile queries for Entity Framework (5).

I got it to work, the only problem is that the value is entered as a query constant, not as a parameter. This reduces the ability of EF to cache the request and reuse it later.

This is what I got so far.

public class MinDateFilter<T> : IFilter<T> where T : class { private readonly Expression<Func<T, bool>> _predicate; public MinDateCandidateFilter(Expression<Func<T, DateTime>> propertySelector, DateTime from) { from = from.Date.AddDays(-1); from = new DateTime(from.Year, from.Month, from.Day, 23, 59, 59, 999); Expression value = Expression.Constant(from, typeof(DateTime)); //ParameterExpression variable = Expression.Variable(typeof(DateTime), "value"); MemberExpression memberExpression = (MemberExpression)propertySelector.Body; ParameterExpression parameter = Expression.Parameter(typeof(T), "item"); Expression exp = Expression.MakeMemberAccess(parameter, memberExpression.Member); Expression operation = Expression.GreaterThan(exp, value); //Expression operation = Expression.GreaterThan(exp, variable); _predicate = Expression.Lambda<Func<T, bool>>(operation, parameter); } public IQueryable<T> Filter(IQueryable<T> items) { return items.Where(_predicate); } } 

This class can be used in two ways:

by subclassification:

 public class MinCreationDateCandidateFilter : MinDateFilter<Candidate> { public MinCreationDateCandidateFilter(DateTime @from) : base(c => c.CreationDate, @from) {} } 

or simply by creating it:

 var filter = new MinDateFilter<Entities.Transition>(t => t.Date, from.Value); 

This is what I have managed to achieve so far:

 SELECT [Extent1].[Id] AS [Id] -- Other fields FROM [dbo].[Candidates] AS [Extent1] WHERE [Extent1].[CreationDate] > convert(datetime2, '1982-12-09 23:59:59.9990000', 121) 

instead

 SELECT [Extent1].[Id] AS [Id] -- Other fields FROM [dbo].[Candidates] AS [Extent1] WHERE [Extent1].[CreationDate] > @p__linq__0 

If I uncomment the two commented lines, and I comment the two above, I get an error message indicating that the value parameter is not connected.

Hope I gave all the useful details :)

+6
source share
1 answer

When a parameter is passed as a ConstantExpression , for example:

 Expression.Constant(myString) 

... it will create a fixed constant character in the resulting query:

 SELECT [Extent1].[Id] AS [Id], [Extent1].[Bar] AS [Bar], FROM [dbo].[Foo] AS [Extent1] WHERE [Extent1].[Bar] = "Some text" 

If you use any tool to parse an expression like (f => f.Bar == myString)) , as I did with the expression tree visualizer, you see that this parameter is actually equal to MemberExpression . So, if you need a parameter for your resulting query, you should pass something like an object property or a more convenient anonymous type:

 Expression.Property( Expression.Constant(new { Value = myString }), "Value" ) 

Thus, you pass the property of the newly created object, and your expression tree receives MemberExpression , resulting in the following CommandText :

 SELECT [Extent1].[Id] AS [Id], [Extent1].[Bar] AS [Bar], FROM [dbo].[Foo] AS [Extent1] WHERE [Extent1].[Bar] = @p__linq__0 
+8
source

Source: https://habr.com/ru/post/949116/


All Articles