Ну, немного поиграв, я получил то, что хотел.
В данном случае это не спасло меня от тонны кода, но значительно облегчает просмотр базового запроса. Для более сложных запросов в будущем это будет здорово! Эта логика запроса никогда не повторяется, но все равно используется повторно столько раз, сколько мне нужно.
Сначала у меня есть два метода в моем хранилище. Один подсчитывает общее количество сообщений (тот, который я использовал в качестве примера в моем вопросе), а другой, который фактически получает коллекцию сообщений по номеру страницы. Вот как они структурированы:
Тот, который получает общее количество сообщений:
/// <summary>
/// Retrieves the total number of messages for the user.
/// </summary>
/// <param name="username">The name of the user.</param>
/// <param name="sent">True if retrieving the number of messages sent.</param>
/// <returns>The total number of messages.</returns>
public int GetMessageCountBy_Username(string username, bool sent)
{
var query = _dataContext.Messages
.Count(UserSelector(username, sent));
return query;
}
Тот, который получает сообщения и страницы их:
/// <summary>
/// Retrieves a list of messages from the data context for a user.
/// </summary>
/// <param name="username">The name of the user.</param>
/// <param name="page">The page number.</param>
/// <param name="itemsPerPage">The number of items to display per page.</param>
/// <returns>An enumerable list of messages.</returns>
public IEnumerable<Message> GetMessagesBy_Username(string username, int page, int itemsPerPage, bool sent)
{
var query = _dataContext.Messages
.Where(UserSelector(username, sent))
.OrderByDescending(x => x.SentDate)
.Skip(itemsPerPage * (page - 1))
.Take(itemsPerPage);
return query;
}
Очевидно, что вызов UserSelector(string, bool)
- вот что важно. Вот как выглядит этот метод:
/// <summary>
/// Builds an expression to be reused in a LINQ query.
/// </summary>
/// <param name="username">The name of the user.</param>
/// <param name="sent">True if retrieving sent messages.</param>
/// <returns>An expression to be used in a LINQ query.</returns>
private Expression<Func<Message, bool>> UserSelector(string username, bool sent)
{
return x => ((sent ? x.FromUser : x.ToUser).Username.ToLower() == username.ToLower()) && (sent ? !x.SenderDeleted : !x.RecipientDeleted);
}
Таким образом, этот метод создает выражение для оценки и правильно переводится в его эквивалент SQL. Функция в выражении оценивается как истина, если имя пользователя совпадает с именем пользователя отправителя или получателя, а удаленное - ложь для отправителя или получателя на основе предоставленного логического значения sent
, которое сериализовано в выражение.
Вот версия выше, которая ближе к примеру в моем вопросе. Это не так читаемо, потому что мое выражение гротескно, но сейчас я понимаю, как оно работает:
public int GetMessageCountBy_Username(string username, bool sent)
{
Expression<Func<Message, bool>> userSelector = x => ((sent ? x.FromUser : x.ToUser).Username.ToLower() == username.ToLower()) && (sent ? !x.SenderDeleted : !x.RecipientDeleted);
var query = _dataContext.Messages
.Count(userSelector);
return query;
}
Это на самом деле довольно классная штука. Потребовалось много времени, чтобы понять, но это кажется действительно мощным. Теперь у меня новое понимание того, как работают LINQ, lambdas и выражения:)
Спасибо всем, кто внес свой вклад в этот вопрос! (включая тебя, артпластика, я все еще люблю тебя, даже если я не люблю твой ответ)