Я боролся с ситуацией, в которой я не нашел решения на данный момент. У меня есть приложение. net core 3.1, использующее EF Core, которое должно отслеживать время выполнения задач для пользователей, у этих пользователей не может быть перекрывающегося времени.
Например, пользователь 1 не может работать с 10:00 до 11:00 указанного дня c дважды или с 9:30 до 10:20, а также с 10:00 до 11:00, поскольку время перекрывается.
Я храню StartDate и EndDate этих задач, а также UserId в моей таблице TaskTime.
Я успешно провел проверки на уровне приложения и триггер в базе данных, который остановит это происходит, кроме одного сценария:
Если у меня есть два или более запросов, приходящих в одно и то же время или в течение миллисекунд друг от друга, проверки приложения бесполезны, поскольку обе вставки будут выполняться одновременно, и база данных триггер не останавливает вставку перекрывающихся записей, либо триггер вообще не выполняется в этих сценариях ios, либо он фактически выполняется, но откат, похоже, не работает. Триггер работает в «медленном сценарии», например, когда один пользователь добавляет одну запись за раз, и время между этими запросами остается свободным.
Вот мой триггер:
create trigger NoOverlappingRanges on TaskTime for insert, update as
begin
if exists(
select 1 from TaskTime TT inner join inserted NV on
(
(
TT.UserId = NV.UserId
and
TT.StartDate <= NV.EndDate
and
NV.StartDate <= TT.EndDate
and
TT.Id <> NV.Id
)
)
)
begin
raiserror('Time ranges should not overlap', 16, 1)
rollback transaction
end
end
Что касается прикладного уровня, потоки не будут знать о существовании друг друга, пока не станет слишком поздно, и если параллелизма не будет, они будут работать отлично.
Большое спасибо всем за чтение и любую возможную помощь .
Я добавлю здесь соответствующий код приложения, чтобы быть более подробным:
public async Task<RequestFeedback> AddTime(int TaskId, Guid UserId, Guid CreatedById, DateTime StartDate, DateTime EndDate, int TimeOffset = 0)
{
RequestFeedback request = new RequestFeedback();
try
{
EndDate = EndDate.ToUTCData(TimeOffset);
StartDate = StartDate.ToUTCData(TimeOffset);
TaskTime time = new TaskTime
{
CreatedById = CreatedById,
Disabled = false,
EndDate = EndDate,
RowDate = DateTime.UtcNow,
StartDate = StartDate,
TaskId = TaskId,
UserId = UserId
};
ICollection<TaskTime> times = await db.TaskTime.Where(a => a.UserId == UserId && !a.EndDate.HasValue).ToListAsync();
if (times.Count > 1)
{
db.RemoveRange(times);
await db.SaveChangesAsync();
request.Success = false;
request.Title = "There were multiple times being tracked";
request.Text = "To avoid time conflicts and overlaps, all open tracked times where cancelled";
return request;
}
ICollection<DateTime> Overlaps = await db.TaskTime.Where(a => a.UserId == UserId && a.StartDate <= time.EndDate && time.StartDate <= a.EndDate.Value).OrderBy(a => a).Select(a => a.StartDate).ToListAsync();
if (Overlaps.Any())
{
DateTime Overlap = Overlaps.FirstOrDefault();
time.EndDate = Overlap.AddSeconds(-1);
if (time.EndDate < time.StartDate)
{
request.Success = false;
request.Title = "Could not add this time slot with a proper timeframe";
request.Text = "The given time would either overlap with other time slots or be smaller than 0 seconds.";
}
else
{
db.Add(time);
await db.SaveChangesAsync();
request.Success = true;
request.Title = "Time slot added successfully";
request.Text = $"Due to overlaps with other time slots, this end date was set to {time.EndDate.FromUTCData(TimeOffset).DateFormat()} to avoid conflicts.";
}
}
else
{
if (time.EndDate < time.StartDate)
{
request.Success = false;
request.Title = "Could not add this time slot with a proper timeframe";
request.Text = "The given time would be smaller than 0 seconds.";
}
else
{
db.Add(time);
await db.SaveChangesAsync();
request.Success = true;
request.Title = "Time slot added successfully";
}
}
return request;
}
catch (Exception e)
{
await errorLogService.InsertException(e); throw;
}
}
Еще раз, спасибо за ваше время