Linq WHERE EF.Functions.Like - Why direct properties work and reflection does not?

.net-core c# entity-framework-core linq reflection

Question

I try to perform a simple LIKE action on the database site, while having query building services based on generic types. I found out while debugging however, that performing EF.Functions.Like() with reflection does not work as expected:

The LINQ expression 'where __Functions_0.Like([c].GetType().GetProperty("FirstName").GetValue([c], null).ToString(), "%Test%")' could not be translated and will be evaluated locally..


The code that makes the difference

That works:

var query = _context.Set<Customer>().Where(c => EF.Functions.Like(c.FirstName, "%Test%"));

This throws the warning & tries to resolve in memory:

var query = _context.Set<Customer>().Where(c => EF.Functions.Like(c.GetType().GetProperty("FirstName").GetValue(c, null).ToString(), "%Test%"));

Does the Linq query builder or the EF.Functions not support reflections?

Sorry if the questions seem basic, it's my first attempt with .NET Core :)

1
3
10/10/2019 10:25:22 AM

Accepted Answer

In EF the lambdas are ExpressionTrees and the expressions are translated to T-SQL so that the query can be executed in the database.

You can create an extension method like so:

public static IQueryable<T> Search<T>(this IQueryable<T> source, string propertyName, string searchTerm)
{
    if (string.IsNullOrEmpty(propertyName) || string.IsNullOrEmpty(searchTerm))
    {
        return source;
    }

    var property = typeof(T).GetProperty(propertyName);

    if (property is null)
    {
        return source;
    }

    searchTerm = "%" + searchTerm + "%";
    var itemParameter = Parameter(typeof(T), "item");

    var functions = Property(null, typeof(EF).GetProperty(nameof(EF.Functions)));
    var like = typeof(DbFunctionsExtensions).GetMethod(nameof(DbFunctionsExtensions.Like), new Type[] { functions.Type, typeof(string), typeof(string) });

    Expression expressionProperty = Property(itemParameter, property.Name);

    if (property.PropertyType != typeof(string))
    {
        expressionProperty = Call(expressionProperty, typeof(object).GetMethod(nameof(object.ToString), new Type[0]));
    }

    var selector = Call(
               null,
               like,
               functions,
               expressionProperty,
               Constant(searchTerm));

    return source.Where(Lambda<Func<T, bool>>(selector, itemParameter));
}

And use it like so:

var query = _context.Set<Customer>().Search("FirstName", "Test").ToList();
var query2 = _context.Set<Customer>().Search("Age", "2").ToList();

For reference this was the Customer I used:

public class Customer
{
    [Key]
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public int Age { get; set; }
}
3
10/10/2019 3:55:32 PM

Popular Answer

Simple answer, no.

EntityFramework is trying to covert your where clause in to a SQL Query. There is no native support for reflection in this conversation.

You have 2 options here. You can construct your text outside of your query or directly use property itself. Is there any specific reason for not using something like following?

var query = _context.Set<Customer>().Where(c => EF.Functions.Like(c.FirstName, "%Test%"));


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow