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