// 1. detect any dirty entities // 2. schedule any entity updates // 3. search out any reachable collections private void FlushEntities(FlushEvent @event) { log.Debug("Flushing entities and processing referenced collections"); // Among other things, updateReachables() will recursively load all // collections that are moving roles. This might cause entities to // be loaded. // So this needs to be safe from concurrent modification problems. // It is safe because of how IdentityMap implements entrySet() IEventSource source = @event.Session; ICollection list = IdentityMap.ConcurrentEntries(source.PersistenceContext.EntityEntries); foreach (DictionaryEntry me in list) { // Update the status of the object and if necessary, schedule an update EntityEntry entry = (EntityEntry)me.Value; Status status = entry.Status; if (status != Status.Loading && status != Status.Gone) { FlushEntityEvent entityEvent = new FlushEntityEvent(source, me.Key, entry); IFlushEntityEventListener[] listeners = source.Listeners.FlushEntityEventListeners; foreach (IFlushEntityEventListener listener in listeners) { listener.OnFlushEntity(entityEvent); } } } source.ActionQueue.SortActions(); }
//process cascade save/update at the start of a flush to discover //any newly referenced entity that must be passed to saveOrUpdate(), //and also apply orphan delete private void PrepareEntityFlushes(IEventSource session) { log.Debug("processing flush-time cascades"); ICollection list = IdentityMap.ConcurrentEntries(session.PersistenceContext.EntityEntries); //safe from concurrent modification because of how entryList() is implemented on IdentityMap foreach (DictionaryEntry me in list) { EntityEntry entry = (EntityEntry)me.Value; Status status = entry.Status; if (status == Status.Loaded || status == Status.Saving || status == Status.ReadOnly) { CascadeOnFlush(session, entry.Persister, me.Key, Anything); } } }
//process cascade save/update at the start of a flush to discover //any newly referenced entity that must be passed to saveOrUpdate(), //and also apply orphan delete protected virtual async Task PrepareEntityFlushesAsync(IEventSource session, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); log.Debug("processing flush-time cascades"); ICollection list = IdentityMap.ConcurrentEntries(session.PersistenceContext.EntityEntries); //safe from concurrent modification because of how entryList() is implemented on IdentityMap foreach (DictionaryEntry me in list) { EntityEntry entry = (EntityEntry)me.Value; Status status = entry.Status; if (status == Status.Loaded || status == Status.Saving || status == Status.ReadOnly) { await(CascadeOnFlushAsync(session, entry.Persister, me.Key, Anything, cancellationToken)).ConfigureAwait(false); } } }
public void ConcurrentEntries() { IDictionary map = GetIdentityMap(); map.Add(noHashCode1, value1); map.Add(noHashCode2, value2); // call ConcurrentEntries and verify it doesn't use the HashCode to build the // new list. ICollection concurrent = IdentityMap.ConcurrentEntries(map); Assert.AreEqual(2, concurrent.Count, "There are two elements in concurrent Map"); foreach (DictionaryEntry de in concurrent) { NoHashCode noCode = (NoHashCode)de.Key; object noCodeValue = de.Value; Assert.IsTrue(map.Contains(noCode), "The Key in the concurrent map should have been in the original map's Keys"); Assert.IsTrue(noCodeValue == map[noCode], "The Value identified by the Key in concurrent map should be the same as the IdentityMap"); } }