以下是代码,它非常简单。运行此作业每30分钟,它会发现约会(通常最多2个)将约会分配给客户会话。保存约会以使其不再完整,并更新客户会话。
public void CompleteAppointments()
{
IValidationManager validationManager = new ValidationManager(_repository);
// Repository starts a new transaction when injected (not shown)
var appointment = _repository.Query<Appointment>(x => x.EndTime < DateTime.Now && !x.Completed).Take(1).FirstOrDefault();
while (appointment != null)
{
_clientSessionService.SetSessionsForClients(appointment);
appointment.Completed = true;
appointment.Clients.Where(c => c.ClientStatus != null && c.ClientStatus.AdminAlerted).ForEachItem(c => c.ClientStatus.AdminAlerted = false);
validationManager = _saveEntityService.ProcessSave(appointment, validationManager); // validates the input and calls save
// commits or rollsback the transaction
validationManager.Finish(); // calls commit or rollback on the session
appointment = _repository.Query<Appointment>(x => x.EndTime < DateTime.Now && !x.Completed).Take(1).FirstOrDefault();
}
}
public virtual void SetSessionsForClients(Appointment apt)
{
apt.Clients.ForEachItem(x=>SetSessionForClient(x, apt));
}
private void SetSessionForClient(Client client, Appointment apt)
{
var sessions = client.Sessions.Where(s => !s.SessionUsed && s.AppointmentType == apt.AppointmentType);
var session = new Session();
if (sessions.Any())
{
session = sessions.OrderBy(s => s.CreatedDate).First();
session.Appointment = apt;
session.Trainer = apt.Trainer;
session.SessionUsed = true;
}
else
{
session = new Session
{
Appointment = apt,
Trainer = apt.Trainer,
InArrears = true,
AppointmentType = apt.AppointmentType,
SessionUsed = true
};
client.AddSession(session);
}
}
有时,每3天左右,它会做一些非常可怕的事情。 SetSessionForClient的第一行(客户端客户端,Appointment apt)读取
var sessions = client.Sessions.Where(s => !s.SessionUsed && s.AppointmentType == apt.AppointmentType);
会发生什么,它会返回一个s.SessionUsed == TRUE的会话!因此,它会参加已经与约会相关联的会话,并为其指定一个新约会。覆盖旧的约会完全杀了我的簿记!
我有日志(为了清楚起见,我省略了它们),显示会话,约会和客户端正在更新,然后是同一会话,更新中的同一客户端不同的约会。
我无法理解它,它开始造成严重破坏。通常当我无法看到任何押韵或理由时,我意味着我做了一些可怕的错误。如果您发现可能导致此问题的任何事情,请告诉我。
sql日志显示它为客户端获取所有会话(这不是意料之外的)我已经记录了一个星期左右的sql并且有一次发生。日志显示会话正在更新,SessionUsed值为true。两次。所以可能的嫌疑人名单很短。
1)nhibernate记录sql,但它现在以某种方式在db
上运行2)nhibernate保存,但在查询时返回并填充对象,但返回的数据是SessionUsed = false,或者只是使用SessionUsed = false
错误地填充对象3)linq,在调用where(x =&gt;!SessionUsed)时无效。
---编辑---
所以一些好人问我关于我的创建新会话和交易流程。
因此,如果提交成功,我就不会创建新会话。我相信我应该这样做。然而,这个应用程序已经生产(虽然轻度使用)约3年,我没有遇到这些问题。我在此之前写的代码更长。所以我不确定会话和交易工作流程是否正确,但是又一次工作了很长时间。
protected void CreateNewCurrentSession() { if (CurrentSession != null) { CurrentSession.Clear(); CurrentSession.Close(); }
if (CurrentStatelessSession != null)
{
CurrentStatelessSession.Close();
}
var session = SessionFactory.OpenSession();
// should probably make this value somehow injected or modifyable
session.FlushMode = FlushMode.Commit;
CurrentSession = session;
CurrentStatelessSession = SessionFactory.OpenStatelessSession();
}