C# 在DataContext.SubmitChanges()期间防止递归
这有点矫揉造作,我希望你会发现这个具有挑战性的问题和我一样有趣……:) 我有一个子类C# 在DataContext.SubmitChanges()期间防止递归,c#,linq,linq-to-sql,C#,Linq,Linq To Sql,这有点矫揉造作,我希望你会发现这个具有挑战性的问题和我一样有趣……:) 我有一个子类DataContext,名为MyDataContext,其中我用以下形式的代码重写了SubmitChanges()方法: BeginTransaction(); // my own implementation IList<object> Updates = GetChangeSet().Updates; foreach (object obj in Updates) { MyClass mc =
DataContext
,名为MyDataContext
,其中我用以下形式的代码重写了SubmitChanges()
方法:
BeginTransaction(); // my own implementation
IList<object> Updates = GetChangeSet().Updates;
foreach (object obj in Updates) {
MyClass mc = obj as MyClass;
if (mc != null)
mc.BeforeUpdate(); // virtual method in MyClass to allow pre-save processing
}
// This is followed by similar code for the Deletes and Inserts, then:
base.SubmitChanges();
// Then do post-save processing...
foreach (object obj in Updates) {
MyClass mc = obj as MyClass;
if (mc != null)
mc.AfterUpdate(); // virtual method in MyClass to allow post-save processing
}
// similar code for Inserts and Deletes
// ...
CommitTransaction();
// obviously all enclosed in a try-catch block where the catch does a rollback
BeginTransaction();//我自己的实现
IList Updates=GetChangeSet()。更新;
foreach(更新中的对象对象对象){
MyClass mc=作为MyClass的obj;
如果(mc!=null)
mc.BeforeUpdate();//MyClass中允许预保存处理的虚拟方法
}
//接下来是删除和插入的类似代码,然后:
base.SubmitChanges();
//然后进行保存后处理。。。
foreach(更新中的对象对象对象){
MyClass mc=作为MyClass的obj;
如果(mc!=null)
mc.AfterUpdate();//MyClass中允许保存后处理的虚拟方法
}
//插入和删除的类似代码
// ...
提交交易();
//显然,所有这些都包含在try-catch块中,catch在其中执行回滚
到目前为止,一切顺利。但是,如果MyClass
的实现在其BeforeUpdate()
或AfterUpdate()方法中调用SubmitChanges()
,就会出现一个小问题。现在我们有一个递归,它很容易导致堆栈溢出
解决这个问题的一种方法是在SubmitChanges()
的开头有一个递归阻塞变量。但是,如果存储被阻止,该怎么办?我不能把它拆分成新的线程;调用线程可能要求SubmitChanges()
调用是同步的,例如,如果它需要在保存后立即访问自动编号属性
另一个要考虑的因素是,如果在前处理或后处理过程中有任何对象被改变,我也希望其<代码>前()/<代码>和<代码>后备()> <代码>方法。< /P>
有没有什么巧妙的教科书方法能把这一切干净利落地做好?我唯一的想法就是在你工作时创造一种缓冲;存储要保存的对象
大概是这样的:
class MyDataContext : DataContext
{
private bool _WorkingFlag = false; // indicates whether we're currently saving
private List<object> _UpdateBuffer = new List<object>();
// ... other buffers here
protected void BeginTransaction()
{
// implementation
}
protected void CommitTransaction()
{
// implementation
}
public override void SubmitChanges()
{
BeginTransaction();
IList<object> updates = GetChangeSet().Updates;
// also inserts and deletes
if (_WorkingFlag)
{
_UpdateBuffer.AddRange(updates);
// also inserts and deletes
return;
}
_WorkingFlag = true;
updates = updates.Concat(_UpdateBuffer).ToList(); // merge the updates with the buffer
foreach (object obj in updates) // do the stuff here...
{
MyClass mc = obj as MyClass;
if (mc != null)
mc.BeforeUpdate(); // virtual method in MyClass to allow pre-save processing
}
_UpdateBuffer.Clear(); // clear the buffer
// ... same for inserts and deletes ...
base.SubmitChanges();
// ... after submit, simply foreach ...
CommitTransaction();
_WorkingFlag = false;
// of course all in try... catch, make sure _WorkingFlag is set back to false in the finally block
}
}
类MyDataContext:DataContext
{
private bool _WorkingFlag=false;//表示当前是否保存
私有列表_UpdateBuffer=新列表();
//…这里还有其他缓冲区
受保护的void BeginTransaction()
{
//实施
}
受保护的无效提交交易()
{
//实施
}
公共覆盖无效提交更改()
{
BeginTransaction();
IList updates=GetChangeSet()。更新;
//也可以插入和删除
如果(_工作标志)
{
_UpdateBuffer.AddRange(更新);
//也可以插入和删除
返回;
}
_WorkingFlag=true;
updates=updates.Concat(_UpdateBuffer).ToList();//将更新与缓冲区合并
foreach(更新中的对象obj)//在这里做这些事情。。。
{
MyClass mc=作为MyClass的obj;
如果(mc!=null)
mc.BeforeUpdate();//MyClass中允许预保存处理的虚拟方法
}
_UpdateBuffer.Clear();//清除缓冲区
//…对于插入和删除也是如此。。。
base.SubmitChanges();
//…提交后,只需提交。。。
提交交易();
_WorkingFlag=false;
//当然,在try…catch中,确保在finally块中将_WorkingFlag设置回false
}
}
我希望这会很好,我没有测试它
我想到的一种方法是
这是一个递归块
开始时的变量
SubmitChanges()。但是如果
保存被阻止了吗
抛出一个不支持的异常。您不应支持在BeforeChanges上发生的另一次提交更改。。。这正是您正在做的,允许在调用SubmitChanges之前进行一些更改
关于调用BeforeUpdate的更新对象,您可以在原始列表中调用BeforeUpdate之后,在提交更改之前检查是否有新的更新对象,直到没有额外的更新对象为止
这同样适用于AfterUpdate,类似于对内存中的对象进行更改。。。不将更多数据保存到数据库
尝试在系统中的不同实体上添加提交更改,必然会在系统中产生一些性能问题。既然已经给出了答案,下面是我如何实际实施解决方案的:
private bool _Busy = false;
public override void SubmitChanges(ConflictMode failureMode) {
if (_Busy)
return; // no action & no error; just let this SubmitChanges handle all nested submissions.
try {
_Busy = true;
BeginTransaction();
Dictionary<MyClass, bool> myUpdates = new Dictionary<MyClass, bool>();
Dictionary<MyClass, bool> myInserts = new Dictionary<MyClass, bool>();
Dictionary<MyClass, bool> myDeletes = new Dictionary<MyClass, bool>();
SynchronizeChanges(myUpdates, GetChangeSet().Updates);
SynchronizeChanges(myInserts, GetChangeSet().Inserts);
SynchronizeChanges(myDeletes, GetChangeSet().Deletes);
while (myInserts.Any(i => i.Value == false) || myUpdates.Any(u => u.Value == false) || myDeletes.Any(d => d.Value == false)) {
List<MyClass> tmp = myInserts.Where(i => i.Value == false).Select(i => i.Key).ToList();
foreach (MyClass mc in tmp) {
mc.BeforeInsert();
myInserts[lt] = true;
}
tmp = myUpdates.Where(u => u.Value == false).Select(u => u.Key).ToList();
foreach (MyClass mc in tmp) {
mc.BeforeUpdate();
myInserts[lt] = true;
}
tmp = myDeletes.Where(d => d.Value == false).Select(d => d.Key).ToList();
foreach (MyClass mc in tmp) {
mc.BeforeDelete();
myInserts[lt] = true;
}
// before calling base.SubmitChanges(), make sure that nothing else got changed:
SynchronizeChanges(myUpdates, GetChangeSet().Updates);
SynchronizeChanges(myInserts, GetChangeSet().Inserts);
SynchronizeChanges(myDeletes, GetChangeSet().Deletes);
}
base.SubmitChanges(failureMode);
// now the After- methods
foreach (MyClass mc in mcInserts.Keys) {
mc.AfterInsert();
}
foreach (MyClass mc in mcUpdates.Keys) {
mc.AfterUpdate();
}
foreach (MyClass mc in mcDeletes.Keys) {
mc.AfterDelete();
}
CommitTransaction();
} catch {
RollbackTransaction();
throw;
} finally {
_Busy = false;
}
// now, just in case any of the After... functions triggered a change:
if (GetChangeSet().Deletes.Count + GetChangeSet().Inserts.Count + GetChangeSet().Updates.Count > 0)
SubmitChanges();
}
private void SynchronizeChanges(Dictionary<MyClass, bool> mcDict, IList<object> iList) {
var q = iList.OfType<MyClass>().Where(i => !mcDict.ContainsKey(i));
q.ToList().ForEach(i => mcDict[i] = false);
}
private bool\u Busy=false;
公共覆盖无效提交更改(冲突模式故障模式){
如果(忙)
return;//无操作&无错误;只需让此SubmitChanges处理所有嵌套提交即可。
试一试{
_忙=真;
BeginTransaction();
Dictionary myUpdates=新字典();
Dictionary myInserts=新字典();
字典myDeletes=新字典();
同步更改(myUpdates,GetChangeSet().Updates);
同步更改(myInserts,GetChangeSet().Inserts);
同步更改(myDeletes,GetChangeSet().Deletes);
while(myInserts.Any(i=>i.Value==false)| | myUpdates.Any(u=>u.Value==false)| | myDeletes.Any(d=>d.Value==false)){
列出tmp=myInserts.Where(i=>i.Value==false);
foreach(tmp中的MyClass mc){
mc.beforeist();
myInserts[lt]=真;
}
tmp=myUpdates.Where(u=>u.Value==false);
foreach(tmp中的MyClass mc){
mc.BeforeUpdate();
myInserts[lt]=真;
}
tmp=myDeletes.Where(d=>d.Value==false);
foreach(tmp中的MyClass mc){
mc.BeforeDelete();
myInserts[lt]=真;
}
//在调用base.SubmitChanges()之前,请确保未更改任何其他内容:
Sy