Linq2db: фильтр по вложенному полю свойства

#c# #linq2db

#c# #linq2db

Вопрос:

Я использую linq2db ORM в своем проекте, и у меня есть следующий запрос:

 var query =
        from t in _db.Transactions
        from a in _db.Accounts.LeftJoin(a => a.Id == t.AccountId)
        from u in _db.Users.LeftJoin(u => u.Id == a.UserId)
        select Transaction.Build(t, u, a);
  

и метод сборки выглядит следующим образом:

 public static Transaction Build(Transaction transaction, User user, Account account)
{
  account.User = user;
  transaction.Account = account;
  return transaction;
}
  

Таблица Users содержит столбец FullName, и мне интересно, возможно ли фильтровать транзакции по полному имени пользователя?

 if (!string.IsNullOrWhiteSpace(userNameFilter))
{
  query = query.Where(t => t.Account.User.FullName.Contains(userNameFilter));
}
  

Возможно ли достичь этого с помощью linq2db?

Ответ №1:

После вызова заводского метода Transaction.Build(t, u, a) linq2db теряет информацию о сопоставлении полей, и запрос становится пригодным только для материализации, но не для фильтрации. Это верно для любых доступных в настоящее время поставщиков LINQ.

Что вы можете сделать с linq2db, чтобы сделать код повторно используемым — перепишите свою функцию Transaction.Build с помощью ExpressionMethodAttribute :

 static Func<Transaction, User, Account, Transaction> _buildFunc;

static Expression<Func<Transaction, User, Account, Transaction>> BuildImpl()
{
   return (Transaction transaction, User user, Account account) => 
      new Transaction
      {
         Id = transaction.Id,
         ... // copy all needed fields

         Account = new Account 
         {
            Id = account.Id,
            ... // copy all needed fields

            User = user
         }
      }
   );
}

[ExpressionMethod(nameof(BuildImpl))]
public static Transaction Build(Transaction transaction, User user, Account account)
{
   // we left function usable even for non-query scenarios.
   _buildFunc ??= BuildImpl().Compile();
   return _buildFunc(transaction, user, account);
}
  

После этих манипуляций ваш запрос может быть отфильтрован после Transaction.Build вызова.

Под капотом linq2db найдет ExpressionMethodAttribute объявление и заменит Transaction.Build вызов выражением, определенным в BuildImpl функции. Схематично, прежде чем анализировать запрос LINQ, он будет преобразован в следующий вариант:

 var query =
    from t in _db.Transactions
    from a in _db.Accounts.LeftJoin(a => a.Id == t.AccountId)
    from u in _db.Users.LeftJoin(u => u.Id == a.UserId)
    select new Transaction
    {
        Id = t.Id,
        ... // copy all needed fields

        Account = new Account 
        {
            Id = a.Id,
            ... // copy all needed fields

            User = u
        }
    };