我会尝试用简化的控制台应用程序示例来解释问题,但是真正的项目是ASP.NET MVC3应用程序。如何分离并行请求?
具有如下表:
想象以下场景:
- 用户创建一个报告(在
TestReport
线,其中Text
是报告string
内容,并Ready
是一个bool
的标志,说,如果报告准备好处理);默认Ready
设置为false
,即未准备好。 - 用户想要处理报告,然后提交;
Ready
在这里设置为true
。
如果系统尚未处理,系统有机会回收报告。所以,当报告被召回时,Ready
被设置为false
。相反,在处理报告时,会创建一条TestReportRef
中的行,其引用报告为Id
。
现在想象一下,在一个和
- 用户要召回报告同一时刻;
- 该报告被添加到进程列表中;
只要这可能同时发生,就可能发生错误。这是报告将有Ready
== false
,它将在TestReportRef
引用。
下面是该过程的一个简单的控制台例子:
var dc = new TestDataContext('my connection string');
dc.TestReport.InsertOnSubmit(new TestReport
{
Text = "My report content",
Ready = true //ready at once
});
dc.SubmitChanges();
Action recallReport =() =>
{
var _dc = new TestDataContext(cs);
var report = _dc.TestReport.FirstOrDefault(t => t.Ready);
if (report != null && !report.TestReportRef.Any())
{
Thread.Sleep(1000);
report.Ready = false;
_dc.SubmitChanges();
}
};
Action acceptReport =() =>
{
var _dc = new TestDataContext(cs);
var report = _dc.TestReport.FirstOrDefault(t => t.Ready);
if (report != null && !report.TestReportRef.Any())
{
Thread.Sleep(1000);
_dc.TestReportRef.InsertOnSubmit(new TestReportRef
{
FK_ReportId = report.Id
});
_dc.SubmitChanges();
}
};
var task1 = new Task(recallReport);
var task2 = new Task(acceptReport);
task1.Start();
task2.Start();
task1.Wait();
task2.Wait();
foreach (var t in dc.TestReport)
{
Console.WriteLine(string.Format("{0}\t{1}\t{2}", t.Id, t.Text, t.Ready));
}
foreach (var t in dc.TestReportRef)
{
Console.WriteLine("ref id:\t" + t.FK_ReportId);
}
Thread.Sleep(1000);
被添加到被保证,那任务将检查同一个情况。
给出的例子听起来很尴尬,但是,我希望它应该解释我正在处理的问题。
我该如何避免这种情况?制作仓库单身似乎并不是一个好主意。 我应该使用一些共享的互斥锁(一个用于所有Web请求)来分隔写入操作吗? 或者是否有这种情况下应该使用的模式?
这只是我有一个场景的一个简化示例。但是,有几种情况可能会遇到类似的差异。我猜,最好的做法是让这种交叉不可能。
这种方法会导致几乎每个表都添加'version'列。在真正的项目中,任务需要检查不同的表格(每个表格有几行)。锁定所有涉及的线路似乎是一项相当复杂的任务。除此之外,当结果可能不正确时,还需要为每种情况编写回滚操作。当然,我很乐意避免所有这些事情。 – horgh 2013-03-27 06:37:51
乐观的方法也可以通过检查所有字段仍然与您在书写之前阅读的相同。你在使用任何OR/M吗? – 2013-03-27 08:26:59
我正在使用LinqToSql – horgh 2013-03-27 08:30:07