LINQ - сопоставить значение, если нет, вернуть элемент NULL - PullRequest
0 голосов
/ 07 ноября 2019

У меня есть заданная дата, и я хочу получить элемент таблицы, который больше указанной даты, но если он не найден, я хочу вернуть элемент с NULL датой.

Например:

Таблица:

enter image description here

Если заданная дата, например, 2019-10-20, я хочу вернуть элемент с идентификатором 3.

Если заданная дата, например, 2019-11-20, я хочу вернуть элемент с идентификатором 4.

Я пытался сделать это:

var parameter = _db.Parameter
.OrderBy(x => x.EndDate)
.First(givenDate < x.EndDate) || x.EndDate == null);

Но он всегда возвращает последний элемент. Что мне нужно сделать?

Ответы [ 3 ]

2 голосов
/ 07 ноября 2019

Я бы оптимизировал код, НЕ используя LINQ для этого:

var parameter = _db.Parameter[0]; // you may need to handle that there's at least 1 item.
for (int i = 1; i < _db.Parameter.Count; i++)
{
    var param = _db.Parameter[i];
    if (param.EndDate > givenDate)
    { // param is good
        if (parameter.EndDate == null || parameter.EndDate > param.EndDate)
            parameter = param; // replace parameter with param
    }
    else if (parameter.EndDate != null && parameter.EndDate < givenDate)
    { // parameter precedes given date, replace it!
        parameter = param;
    }
}

Это будет повторять ваш список только один раз, в отличие от других предоставленных решений.

Если вы ДОЛЖЕН использовать LINQ и хотите выполнить итерацию один раз, может быть, вы можете использовать нижеприведенное, которое будет возвращать динамическое значение, поэтому вам необходимо преобразовать его обратно в Parameter. Он работает путем замены NULL на DateTime.MaxValue, чтобы при вводе OrderBy записи, которые были NULL, были упорядочены внизу.

var param = _db.Parameter
    .Select(x => new
    {
        ID = x.ID,
        EndDate = (x.EndDate.HasValue) ? x.EndDate : DateTime.MaxValue,
        Value = x.Value
    })
    .OrderBy(x => x.EndDate)
    .FirstOrDefault();

var parameter = new Parameter()
    {
        ID = param.ID,
        EndDate = (param.EndDate == DateTime.MaxValue) ? null : param.EndDate,
        Value = param.Value
    };
2 голосов
/ 07 ноября 2019

Вы можете попробовать это:

var query = _db.Parameter
            .OrderBy(x => x.EndDate)
            .ToList();

var parameter = query.FirstOrDefault(x => givenDate < x.EndDate);

if ( parameter == null )
  parameter = query.FirstOrDefault(x => x.EndDate == null);

if ( parameter == null )
  ...
else
  ...

Мы создаем начальный упорядоченный запрос на дату, беря список для оценки запроса.

Далее мы проверяем, соответствует ли первая данная дата датежелаемый результат.

В противном случае мы пытаемся получить первую нулевую строку.

Затем вы можете управлять окончательным регистром.

Чтобы избежать двойного анализа запроса, вы можете использовать это:

TheTypeOfParameter parameterFound = null;
TheTypeOfParameter parameterHavingDateNotNull = null;
TheTypeOfParameter parameterHavingDateNull = null;

bool foundDateNull = false;
bool foundDateNotNull = false;

foreach ( var item in query )
{
  if ( !foundDateNull && item.EndDate == null )
  {
    parameterHavingDateNull = item;
    foundDateNull = true;
  }
  else
  if ( !foundDateNotNull && item.EndDate > givenDate )
    foundDateNotNull = true;
  if ( !foundDateNotNull )
    parameterHavingDateNotNull = item;
  if ( foundDateNotNull || foundDateNull )
    break;
}

parameterFound = parameterHavingDateNotNull != null
               ? parameterHavingDateNotNull
               : parameterHavingDateNull;

Поскольку я не могу тестировать и отлаживать, я надеюсь, что этот цикл будет работать ...

1 голос
/ 07 ноября 2019

Как указано в комментариях, NULL будет первым после заказа. Что делать, если вы попробуете следующее:

var parameter = _db.Parameter
    .Where(x => (x.EndDate > givenDate) || (x.EndDate == null))
    .OrderBy(x => x.EndDate)
    .Last();

После выбора только более ранних дат будет выбрана самая последняя. Если в списке только один элемент (элемент NULL), этот элемент выбирается.

...