如果队列事件在调用 FireQueuedEvents 期间排队,DevForce 并不总是触发这些事件
DevForce doesn't always fire Queued Events if they are queued during a call to FireQueuedEvents
我们 运行 遇到这样的情况:我们更改实体上的 属性 但 PropertyChanged
事件没有触发。我们正在将此逻辑作为保存的一部分进行处理,因此问题似乎是 DevForce 在保存期间对此类事件进行排队的方式。
查看 LoadingBlock.Dispose() 的代码,我看到了这个:
public void Dispose()
{
this._entityManager.FireQueuedEvents();
this._entityManager.IsLoadingEntity = this._wasLoadingEntity;
}
那里存在竞争条件,您在更改 IsLoadingEntity
属性 之前触发排队的事件。这意味着在 FireQueuedEvents 期间生成的任何新事件都将排队(因为 IsLoadingEntity
仍然为真)但是排队的事件将永远不会被触发,因为我们已经触发了排队的事件(我们知道) .似乎 DevForce 应该在触发事件之前重置 IsLoadingEntity 标志。我认为这会解决我们的问题。
这里有一些代码可能有助于解释我们的案例。我将使用 Merge 调用而不是 SaveChanges,因为它在单元测试中更容易使用:
//Create the main Entity Manager and a test entity
var em = new EntityManager();
var entity = new MyEntity {SID = 123};
em.AttachEntity(entity);
//Create a second copy of the entity and another Entity Manager - this is just so
// we can trigger a merge and see the bad behavior
var copy = new MyEntity { SID = 123, MergeCount = 20 };
var em2 = new EntityManager();
em2.AttachEntity(copy);
//This code is a bit contrived but it's similar to what we are doing in our actual app
em.EntityChanged += (sender, args) =>
{
//If it is a MyEntity that changed and it was from a Merge, increment the MergeCount property
var e = args.Entity as MyEntity;
if (e != null && args.Action == EntityAction.ChangeCurrentAndOriginal)
{
e.MergeCount++;
}
};
//Set up a PropertyChanged event handler to see what properties got changed (according to INotifyPropertyChanged)
var propertiesChanged = new List<string>();
entity.PropertyChanged += (sender, args) => { propertiesChanged.Add(args.PropertyName); };
//Merge the copy entity
em2.CacheStateManager.GetCacheState().Merge(em, RestoreStrategy.Normal);
//At this point, the MergeCount property will be 21 - as expected
Assert.AreEqual(21, entity.MergeCount);
//We should have seen a PropertyChanged event for MergeCount since we changed the property (it was 20 and we set it to 21)
Assert.IsTrue(propertiesChanged.Contains("MergeCount"));
//In the debugger, if we look at em._queuedEvents, we'll see some items in there. One of the items is the PropertyChanged event
// for MergeCount. It 'fired' but was queued...and it will be queued forever because the LoadingBlock is long gone.
我发现我可以从一个空的实体管理器中执行另一个合并,这将导致之前排队的事件被触发。在我们 运行 加入其中的一种情况下,这是一个不错的解决方法。但我担心我们可能在其他地方 运行 遇到了这个问题,并且该解决方法对我们不起作用。
您说得对,应该在 Dispose 逻辑的开头清除 IsLoadingEntity 标志,我们将为此打开一个错误报告。
如果您能够使用 EntityChanging 事件而不是 EntityChanged,这也可能是一个可以尝试的解决方法。更改事件未排队,因此处理程序的执行导致在处理 LoadingBlock 之前处理 PropertyChanged 事件。
我们 运行 遇到这样的情况:我们更改实体上的 属性 但 PropertyChanged
事件没有触发。我们正在将此逻辑作为保存的一部分进行处理,因此问题似乎是 DevForce 在保存期间对此类事件进行排队的方式。
查看 LoadingBlock.Dispose() 的代码,我看到了这个:
public void Dispose()
{
this._entityManager.FireQueuedEvents();
this._entityManager.IsLoadingEntity = this._wasLoadingEntity;
}
那里存在竞争条件,您在更改 IsLoadingEntity
属性 之前触发排队的事件。这意味着在 FireQueuedEvents 期间生成的任何新事件都将排队(因为 IsLoadingEntity
仍然为真)但是排队的事件将永远不会被触发,因为我们已经触发了排队的事件(我们知道) .似乎 DevForce 应该在触发事件之前重置 IsLoadingEntity 标志。我认为这会解决我们的问题。
这里有一些代码可能有助于解释我们的案例。我将使用 Merge 调用而不是 SaveChanges,因为它在单元测试中更容易使用:
//Create the main Entity Manager and a test entity
var em = new EntityManager();
var entity = new MyEntity {SID = 123};
em.AttachEntity(entity);
//Create a second copy of the entity and another Entity Manager - this is just so
// we can trigger a merge and see the bad behavior
var copy = new MyEntity { SID = 123, MergeCount = 20 };
var em2 = new EntityManager();
em2.AttachEntity(copy);
//This code is a bit contrived but it's similar to what we are doing in our actual app
em.EntityChanged += (sender, args) =>
{
//If it is a MyEntity that changed and it was from a Merge, increment the MergeCount property
var e = args.Entity as MyEntity;
if (e != null && args.Action == EntityAction.ChangeCurrentAndOriginal)
{
e.MergeCount++;
}
};
//Set up a PropertyChanged event handler to see what properties got changed (according to INotifyPropertyChanged)
var propertiesChanged = new List<string>();
entity.PropertyChanged += (sender, args) => { propertiesChanged.Add(args.PropertyName); };
//Merge the copy entity
em2.CacheStateManager.GetCacheState().Merge(em, RestoreStrategy.Normal);
//At this point, the MergeCount property will be 21 - as expected
Assert.AreEqual(21, entity.MergeCount);
//We should have seen a PropertyChanged event for MergeCount since we changed the property (it was 20 and we set it to 21)
Assert.IsTrue(propertiesChanged.Contains("MergeCount"));
//In the debugger, if we look at em._queuedEvents, we'll see some items in there. One of the items is the PropertyChanged event
// for MergeCount. It 'fired' but was queued...and it will be queued forever because the LoadingBlock is long gone.
我发现我可以从一个空的实体管理器中执行另一个合并,这将导致之前排队的事件被触发。在我们 运行 加入其中的一种情况下,这是一个不错的解决方法。但我担心我们可能在其他地方 运行 遇到了这个问题,并且该解决方法对我们不起作用。
您说得对,应该在 Dispose 逻辑的开头清除 IsLoadingEntity 标志,我们将为此打开一个错误报告。
如果您能够使用 EntityChanging 事件而不是 EntityChanged,这也可能是一个可以尝试的解决方法。更改事件未排队,因此处理程序的执行导致在处理 LoadingBlock 之前处理 PropertyChanged 事件。