方案
我正在编写一个系统来执行考试。考试负责人(监考人员)开始考试,此时,参加考试的人(考生)可以开始考试。如果候选人试图提前开始,他们会收到一条消息,告诉他们等待监考人员。记录两个开始时间(考试开始和候选开始)。
当监考人员开始考试时,我们为考试设置exam start time
,然后我们会找出每个考生在考试中可以有多少时间,然后将其添加到exam start time
,从而给我们称之为scheduled finish
。
public void SetExamStart(Exam exam, List<ExamCandidate> examCandidates)
{
DateTime startTime = _timeService.GetCurrentDateTime();
exam.ExamStarted = startTime;
_work.ExamRepository.Update(exam);
examCandidates.ForEach(ec =>
{
ExamPaper examPaper = ec.ExamPaper;
if (!examPaper.ExamDuration.HasValue)
{
return;
}
Int32 examPaperDuration = examPaper.ExamDuration.Value;
DateTime scheduledFinish = startTime.AddMinutes(examPaperDuration);
ec.ScheduledFinish = scheduledFinish;
_work.ExamCandidateRepository.Update(ec);
});
}
对存储库的调用属于同一事务。 ConnectionService
是注入存储库的依赖项,并且基于每个Web请求将存储库注入UnitOfWork
(_work)。
public virtual void Update(TEntity entity)
{
IDbConnection connection = ConnectionService.Connection;
try
{
connection.Execute(_queryGenerator.UpdateQuery, entity, ConnectionService.Transaction);
}
catch (SqlException ex)
{
throw new DataAccessException("An error occured on query execution", ex);
}
}
当候选人试图开始时:
public void StartCandidateExam(Guid examCandidateId)
{
ExamCandidate examCandidate = _work.ExamCandidateRepository.Get(examCandidateId);
Exam exam = examCandidate.Exam;
if (!exam.ExamStarted.HasValue) {
throw new ExamNotStartedException("Please wait for the invigilator to start the exam");
}
_candidateExamService.StartExam(examCandidate);
_work.SaveChanges();
}
public void StartExam(ExamCandidate examCandidate)
{
DateTime currentTime = _timeService.GetCurrentDateTime();
examCandidate.Started = currentTime;
_work.ExamCandidateRepository.Update(examCandidate);
}
在整个项目中,我对某些实体使用了Lazy<T>
。例如ExamPaper
实体上的ExamCandidate
属性。如下所示:
public class ExamCandidate
{
//other properties
public DateTime? ScheduledFinish { get; set; }
public int ExamPaperID { get; set; }
public LazyEntity<ExamPaper> ExamPaper { get; set; }
}
public class LazyEntity<T> : Lazy<T>
{
public LazyEntity(Func<T> func) : base(func) {}
public static implicit operator T(LazyEntity<T> lazy)
{
return lazy.Value;
}
}
// setting the lazy in the exam candidate repository
examCandidate.ExamPaper = new LazyEntity<ExamPaper>(() =>
{
return Work.ExamPaperRepository.Get(examCandidate.ExamPaperID);
});
问题:
一名考生在监考人员之后很快就开始了他们的考试非常,导致他们的ScheduledFinish没有被设置。这对于几秒钟后开始的其他候选人来说效果很好。
以下是日志:
+------------------------+-------------------------+
| Log Type | Log Date |
+------------------------+-------------------------+
| Exam Started | 2018-03-08 15:00:58.370 |
| Candidate Exam Started | 2018-03-08 15:00:58.387 |
+------------------------+-------------------------+
+-------------------------+-------------------------+--------+-------------+
| Started | ScheduledFinish | ExamID | ExamPaperID |
+-------------------------+-------------------------+--------+-------------+
| 2018-03-08 15:00:58.387 | NULL | 42 | 34 |
| 2018-03-08 15:01:01.727 | 2018-03-08 15:30:58.370 | 42 | 34 |
| 2018-03-08 15:01:02.507 | 2018-03-08 15:30:58.370 | 42 | 56 |
| 2018-03-08 15:01:02.770 | 2018-03-08 15:30:58.370 | 42 | 56 |
| 2018-03-08 15:01:02.960 | 2018-03-08 15:30:58.370 | 42 | 34 |
+-------------------------+-------------------------+--------+-------------+
我看不出这个原因,考试的开始和每个预定完成的设置都是在相同的操作中完成的,在相同的交易下,候选人在这种情况发生之前无法启动。
我有什么遗失的吗?
- 编辑 澄清一下,这个问题在一年之内发生了两次。在监考人员开始后,候选人很快就开始了。
答案 0 :(得分:0)
我认为你的问题是你首先在SetExamStart中设置exam.ExamStarted = startTime而在候选人中设置ScheduledFinish。可以调用StartCandidateExam,因为ExamStarted.HasValue为true,但您的ExamCandidate ScheduledFinish可以为null。
将SetExamStart更改为此以确保已设置ScheduledFinish:
public void SetExamStart(Exam exam, List<ExamCandidate> examCandidates)
{
DateTime startTime = _timeService.GetCurrentDateTime();
examCandidates.ForEach(ec =>
{
ExamPaper examPaper = ec.ExamPaper;
if (!examPaper.ExamDuration.HasValue)
{
return;
}
Int32 examPaperDuration = examPaper.ExamDuration.Value;
DateTime scheduledFinish = startTime.AddMinutes(examPaperDuration);
ec.ScheduledFinish = scheduledFinish;
_work.ExamCandidateRepository.Update(ec);
});
exam.ExamStarted = startTime;
_work.ExamRepository.Update(exam);
}
您可能必须更改此logik
if (!examPaper.ExamDuration.HasValue)
...
太