#c# #asp.net #.net #entity-framework #entity-framework-core
Вопрос:
У меня есть список таких дат, как эта:
DateTime now = DateTime.Today;
var expiriesDays = new List<int> { 1, 7, 14 };
List<DateTime> expiriesDates = expiriesDays.Select(x => now.AddDays(x).Date).ToList();
Теперь у меня есть одна таблица , и в ней есть поле с именем ComplianceExpiryDate
, теперь я хочу проверить, имеет ли это поле какое-либо значение expiriesDates
.
Как и любая запись, имеет значение 13-07-2021, 19-07-2021 ИЛИ 26-07-2021 в ComplianceExpiryDate
Я просто хочу сравнить с Датой, а не со временем.
Я пробовал, TruncateTime
но, DbFunctions
не показывает эту функцию для использования и выдает ошибку.
Ответ №1:
Вы всегда должны быть осторожны при преобразовании значений базы данных перед фильтрацией. Потому что именно это произойдет при применении AddDays
ComplianceExpiryDate
или усечении его временной части, а затем при сравнении результатов с заданным значением даты. База данных должна преобразовать значения всей таблицы, прежде чем приступать к их сравнению. Это может быть дорого, но еще хуже то, что любой индекс в поле поиска теперь непригоден для использования. Это явление отражено в одной короткой фразе: не поддается сарказму.
Поэтому всегда старайтесь фильтровать на основе исходных значений базы данных. С датами это часто сводится к проверке того, находится ли дата в каком-то интервале дат. Подход здесь заключается в построении дневного интервала для каждой даты истечения срока действия.
Короче говоря, создайте интервалы дат, между которыми вы хотите найти ComplianceExpiryDate
, и создайте запрос с условиями «между датой начала и датой окончания», объединенными OR
. Лучше всего это сделать с помощью конструктора предикатов, например Linqkit или этого.
Затем код выглядит следующим образом:
DateTime now = DateTime.Today;
var expiryDays = new List<int> { 1, 7, 14 };
var expirationIntervals = expiryDays
.Select(x => (StartDate: now.AddDays(x - 1), EndDate: now.AddDays(x))).ToList();
Expression<Func<MyTable,bool>> predicate = PredicateBuilder.False<MyTable>();
predicate = expirationIntervals.Aggregate (predicate, (pred, tuple) =>
pred.Or(t => t.ComplianceExpiryDate >= tuple.StartDate
amp;amp; t.ComplianceExpiryDate < tuple.EndDate));
var result = MyTables.Where(predicate);
Этот короткий Or
метод является методом расширения в любом построителе предикатов, который объединяет два предиката в один предикат с помощью токена «OrElse».
Комментарии:
1. Я протестировал это с помощью EF Core 5 и SQLite с некоторыми тестовыми данными. Похоже, вам нужно будет переодеться
.Select(x => (StartDate: now.AddDays(x - 1), EndDate: now.AddDays(x)))
.Select(x => (StartDate: now.AddDays(x), EndDate: now.AddDays(x 1)))
. Пожалуйста, обратите внимание, чтоPredicateBuilder.False
это устарело. Но все же это хорошее решение. br2. @РевеТ. Правильно,
PredicateBuilder.False
устарело в Linqkit. Но я всегда использую чистый и простой конструктор предикатов Монти. Что касается интервала, возможно, это просто интерпретация «истекло», возможно, вы правы. В любом случае, ОП кажется счастливым.
Ответ №2:
Это простое решение, которое генерирует SQL с помощью an IN
.
Вам также следует протестировать решение @GertArnold, чтобы увидеть, что лучше всего подходит для вашего случая. Ответ от @GertArnold используется OR
вместо IN
, но, вероятно, будет работать лучше, когда ваша база данных вырастет.
var today = DateTime.Today;
// create a list of dates without time-part specified
var expiryDays = new List<int> { 1, 7, 14 };
var expirationDates = expiryDays
.Select(it => today.AddDays(it))
.ToList();
// insert test data into db
var currentId = 1;
foreach (var currentDate in expirationDates)
{
db.MyTables.Add(new MyTable
{
Id = currentId,
ComplianceExpiryDate = currentDate.AddHours(3) // move time a few hours into the day
});
currentId ;
}
db.SaveChanges();
var numberOfMatchingItems = db.MyTables
.Count(e => expirationDates.Contains(e.ComplianceExpiryDate.Date));
Console.WriteLine(
$"Number of matching dates found in db: {numberOfMatchingItems}");
Выход:
Количество совпадающих дат, найденных в базе данных: 3
Я использовал это определение для класса сущностей с датой:
public class MyTable
{
public int Id { get; set; }
public DateTime ComplianceExpiryDate { get; set; }
}
SQL, созданный EF:
SELECT COUNT(*)
FROM "MyTables" AS "m"
WHERE rtrim(rtrim(strftime('%Y-%m-%d %H:%M:%f', "m"."ComplianceExpiryDate", 'start of day'), '0'), '.') IN ('2021-07-14 00:00:00', '2021-07-20 00:00:00', '2021-07-27 00:00:00')
Комментарии:
1. Я пытаюсь … позвольте мне проверить это и сообщить вам.
2. @Nic: Пожалуйста, обратите внимание, что я обновил свой ответ из-за неправильного понимания вашего случая в моем первом ответе. br
3. Да, я это проверю.
4. @Nic: Спасибо, пожалуйста, посмотрите мой комментарий к выбранному вами ответу, вероятно, это легко исправляемая ошибка в коде. br