Как создавать многократно используемые операторы linq, которые можно включать как часть предложенийwhere, select и даже C#

Место общения программистов C#
Ответить
Anonymous
 Как создавать многократно используемые операторы linq, которые можно включать как часть предложенийwhere, select и даже

Сообщение Anonymous »

Представьте себе следующие классы (значительно упрощенная версия реального варианта использования):
public class Driver
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Country { get; set; }
public IEnumerable Drives { get; set; }

public DateTime? FirstDriveDate => this.Drives.Select(o => o.Date)
.DefaultIfEmpty().Min();

public DateTime? LastDriveDate => this.Drives.Select(o => o.Date)
.DefaultIfEmpty().Max();
}

public class Drive
{
public Int32 Id { get; set; }
public DateTime Date { get; set; }
public Int32 Distance { get; set; }
}

В моем приложении свойства FirstDriveDate' и LastDriveDate играют важную роль и часто визуализируются в пользовательском интерфейсе. Но они также мне очень нужны в запросах LINQ. В предложениях WHERE, в предложениях ORDER BY и в предложениях SELECT. Прямо сейчас, когда у меня есть запрос, который должен использовать эти «свойства», я просто переписываю точную часть LINQ в большем запросе. Но если в будущем «определение» расчета FirstDriveDate изменится, мне также придется вручную обновить все мои запросы LINQ. Есть ли способ переписать эти свойства как «Выражения», чтобы можно было просто интегрировать их в свои запросы?
Особенно потому, что в зависимости от ситуации я хочу, чтобы логика выполнялась в базе данных, а не сначала загружала все объекты в память и только потом применяла фильтр/сортировку.
Например:
var topNewUKDrivers = database.GetAll()
.Where(o => o.FirstDriveDate.Year == DateTime.Today.Year && o.Country == "UK")
.OrderBy(o => o.FirstDriveDate)
.ThenBy(o => o.Name).ToList()

Вместо:
var topNewUKDrivers = database.GetAll()
.Where(o => o.Drives
.Select(p => p.Date)
.DefaultIfEmpty().Min().Year == DateTime.Today.Year
&& o.Country == "UK")
.OrderBy(o => o.Drives.Select(p => p.Date)
.DefaultIfEmpty().Min())
.ThenBy(o => o.Name).ToList()

Я пробовал использовать такие выражения, как следующие:
public static Expression FirstDriveDateEx =>
(driver) => driver.Drives.Select(o => o.Date)
.DefaultIfEmpty().Min();

и это работает, если вы добавите это в предложениеwhere с .Where(FirstDriveDateEx), но не в том случае, если вы хотите объединить его с другими частями/фильтрами.
Решение
Основываясь на ответе Святослава Данилива ниже, я добавил LinqKit в свое решение и обновил свои классы следующим образом:
public class Driver
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Country { get; set; }
public IEnumerable Drives { get; set; }

private static Func FirstDriveDateImpl;
private static Expression FirstDriveDateEx() => driver => driver.Drives.Select(o => o.Date)
.DefaultIfEmpty().Min();

private static Func LastDriveDateImpl;
private static Expression LastDriveDateEx() => driver => driver.Drives.Select(o => o.Date)
.DefaultIfEmpty().Min();

[Expandable(nameof(FirstDriveDateEx))]
public DateTime? FirstDriveDate
{
get
{
FirstDriveDateImpl ??= FirstDriveDateEx().Compile();
return FirstDriveDateImpl(this);
}
}

[Expandable(nameof(LastDriveDateEx))]
public DateTime? LastDriveDate
{
get
{
LastDriveDateImpl ??= LastDriveDateEx().Compile();
return LastDriveDateImpl(this);
}
}
}

public class Drive
{
public Int32 Id { get; set; }
public DateTime Date { get; set; }
public Int32 Distance { get; set; }
}

Обновив свою модель домена таким образом, я теперь могу писать свои запросы, используя свойство FirstDriveDate как в запросах, пользовательском интерфейсе и т. д., как если бы это было просто любое другое обычное свойство. Фильтрация/упорядочение выполняется в базе данных, когда это часть запроса к БД, или в памяти, когда это запрос linq, выполняемый к объектам.
var drivers = db.Drivers.Where(o => o.FirstDriveDate >= DateTime.Today).OrderBy(o => o.FirstDriveDate);
Console.WriteLine(drivers.Count);
var first = drivers.First();
Console.WriteLine(first.FirstDriveDate);
var last = drivers.OrderByDescending(o => o.FirstDriveDate).First();
Console.WriteLine(last.FirstDriveDate);
var select = drivers.Select(o => o.FirstDriveDate).First();
Console.WriteLine(select);


Подробнее здесь: https://stackoverflow.com/questions/798 ... of-a-where
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»