forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCollectionEntry.cs
426 lines (367 loc) · 12.5 KB
/
CollectionEntry.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
using System;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using NHibernate.Action;
using NHibernate.Collection;
using NHibernate.Impl;
using NHibernate.Persister.Collection;
namespace NHibernate.Engine
{
/// <summary>
/// We need an entry to tell us all about the current state
/// of a collection with respect to its persistent state
/// </summary>
[Serializable]
public partial class CollectionEntry
{
private static readonly INHibernateLogger log = NHibernateLogger.For(typeof (CollectionEntry));
/// <summary>session-start/post-flush persistent state</summary>
private object snapshot;
/// <summary>allow the snapshot to be serialized</summary>
private string role;
/// <summary>
/// The <see cref="ICollectionPersister"/> when the Collection was loaded.
/// </summary>
/// <remarks>
/// This can be <see langword="null" /> if the Collection was not loaded by NHibernate and
/// was passed in along with a transient object.
/// </remarks>
[NonSerialized] private ICollectionPersister loadedPersister;
/// <summary>
/// The identifier of the Entity that is the owner of this Collection
/// during the load or post flush.
/// </summary>
private object loadedKey;
/// <summary>
/// Indicates that the Collection can still be reached by an Entity
/// that exist in the <see cref="ISession"/>.
/// </summary>
/// <remarks>
/// It is also used to ensure that the Collection is not shared between
/// two Entities.
/// </remarks>
[NonSerialized] private bool reached;
/// <summary>
/// Indicates that the Collection has been processed and is ready
/// to have its state synchronized with the database.
/// </summary>
[NonSerialized] private bool processed;
/// <summary>
/// Indicates that a Collection needs to be updated.
/// </summary>
/// <remarks>
/// A Collection needs to be updated whenever the contents of the Collection
/// have been changed.
/// </remarks>
[NonSerialized] private bool doupdate;
/// <summary>
/// Indicates that a Collection has old elements that need to be removed.
/// </summary>
/// <remarks>
/// A Collection needs to have removals performed whenever its role changes or
/// the key changes and it has a loadedPersister - ie - it was loaded by NHibernate.
/// </remarks>
[NonSerialized] private bool doremove;
/// <summary>
/// Indicates that a Collection needs to be recreated.
/// </summary>
/// <remarks>
/// A Collection needs to be recreated whenever its role changes
/// or the owner changes.
/// </remarks>
[NonSerialized] private bool dorecreate;
/// <summary>
/// If we instantiate a collection during the <see cref="ISession.Flush" />
/// process, we must ignore it for the rest of the flush.
/// </summary>
[NonSerialized] private bool ignore;
// <summary>
// Indicates that the Collection has been fully initialized.
// </summary>
//private bool initialized;
// For the fields below, "current" means the reference that was found
// during Flush(), and "loaded" means the reference that is consistent
// with the current database state
/// <summary>
/// The <see cref="ICollectionPersister"/> that is currently responsible
/// for the Collection.
/// </summary>
/// <remarks>
/// This is set when NHibernate is updating a reachable or an
/// unreachable collection.
/// </remarks>
[NonSerialized] private ICollectionPersister currentPersister;
[NonSerialized] private object currentKey;
/// <summary>
/// Initializes a new instance of <see cref="CollectionEntry"/>.
/// </summary>
/// <remarks>
/// For newly wrapped collections, or dereferenced collection wrappers
/// </remarks>
public CollectionEntry(ICollectionPersister persister, IPersistentCollection collection)
{
// new collections that get found + wrapped
// during flush shouldn't be ignored
ignore = false;
collection.ClearDirty(); //a newly wrapped collection is NOT dirty (or we get unnecessary version updates)
snapshot = persister.IsMutable ? collection.GetSnapshot(persister) : null;
collection.SetSnapshot(loadedKey, role, snapshot);
}
/// <summary> For collections just loaded from the database</summary>
public CollectionEntry(IPersistentCollection collection, ICollectionPersister loadedPersister, object loadedKey,
bool ignore)
{
this.ignore = ignore;
this.loadedKey = loadedKey;
SetLoadedPersister(loadedPersister);
collection.SetSnapshot(loadedKey, role, null);
//postInitialize() will be called after initialization
}
public CollectionEntry(ICollectionPersister loadedPersister, object loadedKey)
{
// detached collection wrappers that get found + reattached
// during flush shouldn't be ignored
ignore = false;
//collection.clearDirty()
this.loadedKey = loadedKey;
SetLoadedPersister(loadedPersister);
}
/// <summary>
/// Initializes a new instance of <see cref="CollectionEntry"/> for initialized detached collections.
/// </summary>
/// <remarks>
/// For initialized detached collections
/// </remarks>
internal CollectionEntry(IPersistentCollection collection, ISessionFactoryImplementor factory)
{
// detached collections that get found + reattached
// during flush shouldn't be ignored
ignore = false;
loadedKey = collection.Key;
SetLoadedPersister(factory.GetCollectionPersister(collection.Role));
snapshot = collection.StoredSnapshot;
}
/// <summary></summary>
public object Key
{
get { return loadedKey; }
}
/// <summary></summary>
public string Role
{
get { return role; }
set { role = value; }
}
/// <summary></summary>
public object Snapshot
{
get { return snapshot; }
}
public bool IsReached
{
get { return reached; }
set { reached = value; }
}
public bool IsProcessed
{
get { return processed; }
set { processed = value; }
}
public bool IsDoupdate
{
get { return doupdate; }
set { doupdate = value; }
}
public bool IsDoremove
{
get { return doremove; }
set { doremove = value; }
}
public bool IsDorecreate
{
get { return dorecreate; }
set { dorecreate = value; }
}
public bool IsIgnore
{
get { return ignore; }
}
public ICollectionPersister CurrentPersister
{
get { return currentPersister; }
set { currentPersister = value; }
}
public object CurrentKey
{
get { return currentKey; }
set { currentKey = value; }
}
public object LoadedKey
{
get { return loadedKey; }
}
public ICollectionPersister LoadedPersister
{
get { return loadedPersister; }
}
public bool WasDereferenced
{
get { return loadedKey == null; }
}
/// <summary>
/// Determine if the collection is "really" dirty, by checking dirtiness
/// of the collection elements, if necessary
/// </summary>
private void Dirty(IPersistentCollection collection)
{
// if the collection is initialized and it was previously persistent
// initialize the dirty flag
bool forceDirty = collection.WasInitialized && !collection.IsDirty && LoadedPersister != null
&& LoadedPersister.IsMutable
&& (collection.IsDirectlyAccessible || LoadedPersister.ElementType.IsMutable)
&& !collection.EqualsSnapshot(LoadedPersister);
if (forceDirty)
{
collection.Dirty();
}
}
/// <summary>
/// Prepares this CollectionEntry for the Flush process.
/// </summary>
/// <param name="collection">The <see cref="IPersistentCollection"/> that this CollectionEntry will be responsible for flushing.</param>
public void PreFlush(IPersistentCollection collection)
{
bool nonMutableChange = collection.IsDirty && LoadedPersister != null && !LoadedPersister.IsMutable;
if (nonMutableChange)
{
throw new HibernateException("changed an immutable collection instance: " + MessageHelper.InfoString(LoadedPersister.Role, LoadedKey));
}
Dirty(collection);
if (log.IsDebugEnabled() && collection.IsDirty && loadedPersister != null)
{
log.Debug("Collection dirty: {0}", MessageHelper.CollectionInfoString(loadedPersister, loadedKey));
}
// reset all of these values so any previous flush status
// information is cleared from this CollectionEntry
doupdate = false;
doremove = false;
dorecreate = false;
reached = false;
processed = false;
}
/// <summary>
/// Updates the CollectionEntry to reflect that the <see cref="IPersistentCollection"/>
/// has been initialized.
/// </summary>
/// <param name="collection">The initialized <see cref="AbstractPersistentCollection"/> that this Entry is for.</param>
//Since v5.1
[Obsolete("Please use PostInitialize(collection, persistenceContext) instead.")]
public void PostInitialize(IPersistentCollection collection)
{
snapshot = LoadedPersister.IsMutable ? collection.GetSnapshot(LoadedPersister) : null;
collection.SetSnapshot(loadedKey, role, snapshot);
}
/// <summary>
/// Updates the CollectionEntry to reflect that the <see cref="IPersistentCollection"/>
/// has been initialized.
/// </summary>
/// <param name="collection">The initialized <see cref="AbstractPersistentCollection"/> that this Entry is for.</param>
/// <param name="persistenceContext"></param>
public void PostInitialize(IPersistentCollection collection, IPersistenceContext persistenceContext)
{
#pragma warning disable 618
//6.0 TODO: Inline PostInitialize here.
PostInitialize(collection);
#pragma warning restore 618
if (LoadedPersister.GetBatchSize() > 1)
{
persistenceContext.BatchFetchQueue.RemoveBatchLoadableCollection(this);
}
}
/// <summary>
/// Updates the CollectionEntry to reflect that it is has been successfully flushed to the database.
/// </summary>
/// <param name="collection">The <see cref="IPersistentCollection"/> that was flushed.</param>
/// <remarks>
/// Called after a <em>successful</em> flush.
/// </remarks>
public void PostFlush(IPersistentCollection collection)
{
if (IsIgnore)
{
ignore = false;
}
else if (!IsProcessed)
{
// the CollectionEntry should be processed if we are in the PostFlush()
throw new AssertionFailure("collection [" + collection.Role + "] was not processed by flush()");
}
collection.SetSnapshot(loadedKey, role, snapshot);
}
public void AfterAction(IPersistentCollection collection)
{
loadedKey = CurrentKey;
SetLoadedPersister(CurrentPersister);
bool resnapshot = collection.WasInitialized && (IsDoremove || IsDorecreate || IsDoupdate);
if (resnapshot)
{
//re-snapshot
snapshot = loadedPersister == null || !loadedPersister.IsMutable ? null : collection.GetSnapshot(loadedPersister);
}
collection.PostAction();
}
/// <summary>
/// Sets the information in this CollectionEntry that is specific to the
/// <see cref="ICollectionPersister"/>.
/// </summary>
/// <param name="persister">
/// The <see cref="ICollectionPersister"/> that is
/// responsible for the Collection.
/// </param>
private void SetLoadedPersister(ICollectionPersister persister)
{
loadedPersister = persister;
Role = (persister == null) ? null : persister.Role;
}
internal void AfterDeserialize(ISessionFactoryImplementor factory)
{
loadedPersister = factory.GetCollectionPersister(role);
}
public ICollection GetOrphans(string entityName, IPersistentCollection collection)
{
if (snapshot == null)
{
throw new AssertionFailure("no collection snapshot for orphan delete");
}
return collection.GetOrphans(snapshot, entityName);
}
//Since 5.3
[Obsolete("This method has no more usages and will be removed in a future version")]
public Task<ICollection> GetOrphansAsync(string entityName, IPersistentCollection collection, CancellationToken cancellationToken)
{
if (snapshot == null)
{
throw new AssertionFailure("no collection snapshot for orphan delete");
}
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<ICollection>(cancellationToken);
}
return collection.GetOrphansAsync(snapshot, entityName, cancellationToken);
}
public bool IsSnapshotEmpty(IPersistentCollection collection)
{
return collection.WasInitialized && (LoadedPersister == null || LoadedPersister.IsMutable) && collection.IsSnapshotEmpty(Snapshot);
}
public override string ToString()
{
string result = "CollectionEntry" + MessageHelper.InfoString(loadedPersister.Role, loadedKey);
if (currentPersister != null)
{
result += ("->" + MessageHelper.InfoString(currentPersister.Role, currentKey));
}
return result;
}
}
}