Linq WHERE EF.Functions.Like - Почему прямые свойства работают, а отражение - нет? - PullRequest
2 голосов
/ 10 октября 2019

Я пытаюсь выполнить простое действие LIKE на сайте базы данных, имея при этом службы построения запросов на основе универсальных типов. Однако при отладке я обнаружил, что выполнение EF.Functions.Like() с отражением не работает должным образом:

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..


Код, который имеет значение

Это работает :

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

Выдает предупреждение и пытается разрешить в памяти :

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

Поддерживает ли построитель запросов Linq или функции EF.Functions отражения?

Извините, если вопросы кажутся базовыми, это моя первая попытка .NET Core:)

Ответы [ 3 ]

2 голосов
/ 10 октября 2019

В EF лямбды равны ExpressionTrees , а выражения транслируются в T-SQL, так что запрос может быть выполнен в базе данных.

Вы можете создать метод расширения следующим образом:

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));
}

И использовать его так:

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

Для справки это был Customer Я использовал:

public class Customer
{
    [Key]
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public int Age { get; set; }
}
1 голос
/ 10 октября 2019

Простой ответ, нет.

EntityFramework пытается преобразовать ваше предложение where в SQL-запрос. В этом разговоре отсутствует встроенная поддержка отражения.

У вас есть 2 варианта здесь. Вы можете создать свой текст вне вашего запроса или напрямую использовать само свойство. Есть ли какая-то конкретная причина не использовать что-то вроде следующего?

var query = _context.Set<Customer>().Where(c => EF.Functions.Like(c.FirstName, "%Test%"));
1 голос
/ 10 октября 2019

Имейте в виду, что каждое ExpresionTree, которое вы вставляете в условие Where, должно переводиться в SQL-запрос.

Из-за этого ExpressionTrees, которые вы можете написать, довольно ограничены, вы должны придерживаться некоторых правил,вот почему отражение не поддерживается.

Изображение, которое вместо:

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

Вы пишете что-то вроде:

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

Это будет означать, что EF может переводитьлюбой код C # для запроса SQL - это явно не так :)

...