Skip to content

Commit 4bdcfbe

Browse files
committed
Convert EntityKey to struct
1 parent 14c5cdb commit 4bdcfbe

14 files changed

+60
-49
lines changed

src/NHibernate/Action/EntityIdentityInsertAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public EntityIdentityInsertAction(object[] state, object instance, IEntityPersis
1919
: base(null, state, instance, persister, session)
2020
{
2121
this.isDelayed = isDelayed;
22-
delayedEntityKey = this.isDelayed ? GenerateDelayedEntityKey() : null;
22+
delayedEntityKey = this.isDelayed ? GenerateDelayedEntityKey() : EntityKey.Null;
2323
}
2424

2525
public object GeneratedId

src/NHibernate/Async/Event/Default/AbstractSaveEventListener.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ protected virtual async Task<object> PerformSaveAsync(object entity, object id,
150150
}
151151
else
152152
{
153-
key = null;
153+
key = EntityKey.Null;
154154
}
155155

156156
if (InvokeSaveLifecycle(entity, persister, source))
@@ -184,7 +184,7 @@ protected virtual async Task<object> PerformSaveOrReplicateAsync(object entity,
184184
cancellationToken.ThrowIfCancellationRequested();
185185
Validate(entity, persister, source);
186186

187-
object id = key == null ? null : key.Identifier;
187+
object id = key.IsNull ? null : key.Identifier;
188188

189189
bool shouldDelayIdentityInserts = !requiresImmediateIdAccess;
190190

src/NHibernate/Async/Event/Default/DefaultReplicateEventListener.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public virtual async Task OnReplicateAsync(ReplicateEvent @event, CancellationTo
102102
}
103103

104104
bool regenerate = persister.IsIdentifierAssignedByInsert; // prefer re-generation of identity!
105-
EntityKey key = regenerate ? null : source.GenerateEntityKey(id, persister);
105+
EntityKey key = regenerate ? EntityKey.Null : source.GenerateEntityKey(id, persister);
106106

107107
await (PerformSaveOrReplicateAsync(entity, key, persister, regenerate, replicationMode, source, true, cancellationToken)).ConfigureAwait(false);
108108
}

src/NHibernate/Async/Impl/MultiCriteriaImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private async Task GetResultsFromDatabaseAsync(IList results, CancellationToken
184184

185185
object o =
186186
await (loader.GetRowFromResultSetAsync(reader, session, queryParameters, loader.GetLockModes(queryParameters.LockModes),
187-
null, hydratedObjects[i], keys, true, cancellationToken)).ConfigureAwait(false);
187+
EntityKey.Null, hydratedObjects[i], keys, true, cancellationToken)).ConfigureAwait(false);
188188
if (createSubselects[i])
189189
{
190190
subselectResultKeys[i].Add(keys);

src/NHibernate/Async/Loader/Loader.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ protected async Task<object> LoadSingleRowAsync(DbDataReader resultSet, ISession
113113
try
114114
{
115115
result =
116-
await (GetRowFromResultSetAsync(resultSet, session, queryParameters, GetLockModes(queryParameters.LockModes), null,
116+
await (GetRowFromResultSetAsync(resultSet, session, queryParameters, GetLockModes(queryParameters.LockModes), EntityKey.Null,
117117
hydratedObjects, new EntityKey[entitySpan], returnProxies, cancellationToken)).ConfigureAwait(false);
118118
}
119119
catch (OperationCanceledException) { throw; }
@@ -178,7 +178,7 @@ internal async Task<object> GetRowFromResultSetAsync(DbDataReader resultSet, ISe
178178
{
179179
object entity = row[i];
180180
var key = keys[i];
181-
if (entity == null && key != null && IsChildFetchEntity(i))
181+
if (entity == null && key.IsNotNull && IsChildFetchEntity(i))
182182
{
183183
// The entity was missing in the session, fallback on internal load (which will just yield a
184184
// proxy if the persister supports it).
@@ -554,7 +554,7 @@ private async Task<EntityKey> GetKeyFromResultSetAsync(int i, IEntityPersister p
554554
}
555555
}
556556

557-
return resultId == null ? null : session.GenerateEntityKey(resultId, persister);
557+
return resultId == null ? EntityKey.Null : session.GenerateEntityKey(resultId, persister);
558558
}
559559

560560
/// <summary>
@@ -610,7 +610,7 @@ private async Task<object[]> GetRowAsync(DbDataReader rs, ILoadable[] persisters
610610
object obj = null;
611611
EntityKey key = keys[i];
612612

613-
if (key == null)
613+
if (key.IsNull)
614614
{
615615
// do nothing
616616
/* TODO NH-1001 : if (persisters[i]...EntityType) is an OneToMany or a ManyToOne and
@@ -736,7 +736,7 @@ private async Task<object> InstanceNotYetLoadedAsync(DbDataReader dr, int i, ILo
736736

737737
string instanceClass = await (GetInstanceClassAsync(dr, i, persister, key.Identifier, session, cancellationToken)).ConfigureAwait(false);
738738

739-
if (optionalObjectKey != null && key.Equals(optionalObjectKey))
739+
if (optionalObjectKey.IsNotNull && key.Equals(optionalObjectKey))
740740
{
741741
// its the given optional object
742742
obj = optionalObject;

src/NHibernate/Engine/EntityEntry.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public EntityKey EntityKey
192192
{
193193
get
194194
{
195-
if (cachedEntityKey == null)
195+
if (cachedEntityKey.IsNull)
196196
{
197197
if (id == null)
198198
throw new InvalidOperationException("cannot generate an EntityKey when id is null.");

src/NHibernate/Engine/EntityKey.cs

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Security;
44
using NHibernate.Impl;
55
using NHibernate.Persister.Entity;
6-
using NHibernate.Type;
76

87
namespace NHibernate.Engine
98
{
@@ -12,28 +11,44 @@ namespace NHibernate.Engine
1211
/// and the identifier space (eg. tablename)
1312
/// </summary>
1413
[Serializable]
15-
public sealed class EntityKey : IDeserializationCallback, ISerializable, IEquatable<EntityKey>
14+
public struct EntityKey : ISerializable, IEquatable<EntityKey>
1615
{
16+
public static EntityKey Null { get; } = new EntityKey();
17+
18+
public bool IsNull => identifier == null;
19+
public bool IsNotNull => !IsNull;
20+
1721
private readonly object identifier;
1822
private readonly IEntityPersister _persister;
1923
// hashcode may vary among processes, they cannot be stored and have to be re-computed after deserialization
20-
[NonSerialized]
21-
private int? _hashCode;
24+
private readonly int _hashCode;
2225

2326
/// <summary> Construct a unique identifier for an entity class instance</summary>
2427
public EntityKey(object id, IEntityPersister persister)
2528
{
2629
identifier = id ?? throw new AssertionFailure("null identifier");
2730
_persister = persister;
31+
_hashCode = 0;
32+
2833
_hashCode = GenerateHashCode();
2934
}
3035

3136
private EntityKey(SerializationInfo info, StreamingContext context)
3237
{
38+
_hashCode = 0;
39+
3340
identifier = info.GetValue(nameof(Identifier), typeof(object));
41+
if (identifier == null)
42+
{
43+
_persister = null;
44+
return;
45+
}
46+
3447
var factory = (ISessionFactoryImplementor) info.GetValue(nameof(_persister.Factory), typeof(ISessionFactoryImplementor));
35-
var entityName = (string) info.GetValue(nameof(EntityName), typeof(string));
48+
var entityName = info.GetString(nameof(EntityName));
49+
3650
_persister = factory.GetEntityPersister(entityName);
51+
_hashCode = GenerateHashCode();
3752
}
3853

3954
public bool IsBatchLoadable => _persister.IsBatchLoadable;
@@ -54,10 +69,8 @@ public override bool Equals(object other)
5469

5570
public bool Equals(EntityKey other)
5671
{
57-
if (other == null)
58-
{
59-
return false;
60-
}
72+
if (other.IsNull)
73+
return IsNull;
6174

6275
return
6376
other.RootEntityName.Equals(RootEntityName)
@@ -66,17 +79,7 @@ public bool Equals(EntityKey other)
6679

6780
public override int GetHashCode()
6881
{
69-
// If the object is put in a set or dictionary during deserialization, the hashcode will not yet be
70-
// computed. Compute the hashcode on the fly. So long as this happens only during deserialization, there
71-
// will be no thread safety issues. For the hashcode to be always defined after deserialization, the
72-
// deserialization callback is used.
73-
return _hashCode ?? GenerateHashCode();
74-
}
75-
76-
/// <inheritdoc />
77-
public void OnDeserialization(object sender)
78-
{
79-
_hashCode = GenerateHashCode();
82+
return _hashCode;
8083
}
8184

8285
private int GenerateHashCode()
@@ -99,6 +102,9 @@ public override string ToString()
99102
public void GetObjectData(SerializationInfo info, StreamingContext context)
100103
{
101104
info.AddValue(nameof(Identifier), identifier);
105+
if (identifier == null)
106+
return;
107+
102108
info.AddValue(nameof(_persister.Factory), _persister.Factory);
103109
info.AddValue(nameof(EntityName), EntityName);
104110
}

src/NHibernate/Engine/StatefulPersistenceContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ public object NarrowProxy(INHibernateProxy proxy, IEntityPersister persister, En
753753
/// </summary>
754754
public object ProxyFor(IEntityPersister persister, EntityKey key, object impl)
755755
{
756-
if (!persister.HasProxy || key == null)
756+
if (!persister.HasProxy || key.IsNull)
757757
return impl;
758758

759759
INHibernateProxy proxy;

src/NHibernate/Event/Default/AbstractSaveEventListener.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ protected virtual object PerformSave(object entity, object id, IEntityPersister
177177
}
178178
else
179179
{
180-
key = null;
180+
key = EntityKey.Null;
181181
}
182182

183183
if (InvokeSaveLifecycle(entity, persister, source))
@@ -209,7 +209,7 @@ protected virtual object PerformSaveOrReplicate(object entity, EntityKey key, IE
209209
{
210210
Validate(entity, persister, source);
211211

212-
object id = key == null ? null : key.Identifier;
212+
object id = key.IsNull ? null : key.Identifier;
213213

214214
bool shouldDelayIdentityInserts = !requiresImmediateIdAccess;
215215

src/NHibernate/Event/Default/DefaultReplicateEventListener.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public virtual void OnReplicate(ReplicateEvent @event)
9595
}
9696

9797
bool regenerate = persister.IsIdentifierAssignedByInsert; // prefer re-generation of identity!
98-
EntityKey key = regenerate ? null : source.GenerateEntityKey(id, persister);
98+
EntityKey key = regenerate ? EntityKey.Null : source.GenerateEntityKey(id, persister);
9999

100100
PerformSaveOrReplicate(entity, key, persister, regenerate, replicationMode, source, true);
101101
}

src/NHibernate/Impl/MultiCriteriaImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ private void GetResultsFromDatabase(IList results)
257257

258258
object o =
259259
loader.GetRowFromResultSet(reader, session, queryParameters, loader.GetLockModes(queryParameters.LockModes),
260-
null, hydratedObjects[i], keys, true);
260+
EntityKey.Null, hydratedObjects[i], keys, true);
261261
if (createSubselects[i])
262262
{
263263
subselectResultKeys[i].Add(keys);

src/NHibernate/Loader/Loader.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ protected object LoadSingleRow(DbDataReader resultSet, ISessionImplementor sessi
300300
try
301301
{
302302
result =
303-
GetRowFromResultSet(resultSet, session, queryParameters, GetLockModes(queryParameters.LockModes), null,
303+
GetRowFromResultSet(resultSet, session, queryParameters, GetLockModes(queryParameters.LockModes), EntityKey.Null,
304304
hydratedObjects, new EntityKey[entitySpan], returnProxies);
305305
}
306306
catch (HibernateException)
@@ -333,7 +333,7 @@ internal static EntityKey GetOptionalObjectKey(QueryParameters queryParameters,
333333
}
334334
else
335335
{
336-
return null;
336+
return EntityKey.Null;
337337
}
338338
}
339339

@@ -377,7 +377,7 @@ internal object GetRowFromResultSet(DbDataReader resultSet, ISessionImplementor
377377
{
378378
object entity = row[i];
379379
var key = keys[i];
380-
if (entity == null && key != null && IsChildFetchEntity(i))
380+
if (entity == null && key.IsNotNull && IsChildFetchEntity(i))
381381
{
382382
// The entity was missing in the session, fallback on internal load (which will just yield a
383383
// proxy if the persister supports it).
@@ -549,7 +549,7 @@ private static ISet<EntityKey>[] Transpose(IList<EntityKey[]> keys)
549549
for (int i = 0; i < keys.Count; i++)
550550
{
551551
EntityKey key = keys[i][j];
552-
if (key != null)
552+
if (key.IsNotNull)
553553
{
554554
result[j].Add(key);
555555
}
@@ -569,7 +569,7 @@ internal void CreateSubselects(IList<EntityKey[]> keys, QueryParameters queryPar
569569
{
570570
for (int i = 0; i < rowKeys.Length; i++)
571571
{
572-
if (rowKeys[i] != null && subSelects[i] != null)
572+
if (rowKeys[i].IsNotNull && subSelects[i] != null)
573573
{
574574
session.PersistenceContext.BatchFetchQueue.AddSubselect(rowKeys[i], subSelects[i]);
575575
}
@@ -759,7 +759,7 @@ private void RegisterNonExists(EntityKey[] keys, ISessionImplementor session)
759759
if (owner > -1)
760760
{
761761
EntityKey ownerKey = keys[owner];
762-
if (keys[i] == null && ownerKey != null)
762+
if (keys[i].IsNull && ownerKey.IsNotNull)
763763
{
764764
bool isOneToOneAssociation = ownerAssociationTypes != null && ownerAssociationTypes[i] != null
765765
&& ownerAssociationTypes[i].IsOneToOne;
@@ -895,7 +895,7 @@ private EntityKey GetKeyFromResultSet(int i, IEntityPersister persister, object
895895
}
896896
}
897897

898-
return resultId == null ? null : session.GenerateEntityKey(resultId, persister);
898+
return resultId == null ? EntityKey.Null : session.GenerateEntityKey(resultId, persister);
899899
}
900900

901901
/// <summary>
@@ -949,7 +949,7 @@ private object[] GetRow(DbDataReader rs, ILoadable[] persisters, EntityKey[] key
949949
object obj = null;
950950
EntityKey key = keys[i];
951951

952-
if (key == null)
952+
if (key.IsNull)
953953
{
954954
// do nothing
955955
/* TODO NH-1001 : if (persisters[i]...EntityType) is an OneToMany or a ManyToOne and
@@ -1077,7 +1077,7 @@ private object InstanceNotYetLoaded(DbDataReader dr, int i, ILoadable persister,
10771077

10781078
string instanceClass = GetInstanceClass(dr, i, persister, key.Identifier, session);
10791079

1080-
if (optionalObjectKey != null && key.Equals(optionalObjectKey))
1080+
if (optionalObjectKey.IsNotNull && key.Equals(optionalObjectKey))
10811081
{
10821082
// its the given optional object
10831083
obj = optionalObject;

src/NHibernate/Proxy/AbstractLazyInitializer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ private void ErrorIfReadOnlySettingNotAvailable()
247247
private static EntityKey GenerateEntityKeyOrNull(object id, ISessionImplementor s, string entityName)
248248
{
249249
if (id == null || s == null || entityName == null)
250-
return null;
250+
return EntityKey.Null;
251251

252252
return s.GenerateEntityKey(id, s.Factory.GetEntityPersister(entityName));
253253
}
@@ -266,7 +266,7 @@ private void CheckTargetState()
266266
private object GetProxyOrNull()
267267
{
268268
EntityKey entityKey = GenerateEntityKeyOrNull(_id, _session, _entityName);
269-
if (entityKey != null && _session != null && _session.IsOpen)
269+
if (entityKey.IsNotNull && _session != null && _session.IsOpen)
270270
{
271271
return _session.PersistenceContext.GetProxy(entityKey);
272272
}
@@ -287,7 +287,7 @@ private void SetReadOnly(bool readOnly)
287287
if (initialized)
288288
{
289289
EntityKey key = GenerateEntityKeyOrNull(_id, _session, _entityName);
290-
if (key != null && _session.PersistenceContext.ContainsEntity(key))
290+
if (key.IsNotNull && _session.PersistenceContext.ContainsEntity(key))
291291
{
292292
_session.PersistenceContext.SetReadOnly(_target, readOnly);
293293
}

src/NHibernate/Util/StringHelper.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ public static bool BooleanValue(string value)
396396
return trimmed.Equals("true", StringComparison.OrdinalIgnoreCase) || trimmed.Equals("t", StringComparison.OrdinalIgnoreCase);
397397
}
398398

399-
private static string NullSafeToString(object obj)
399+
private static string NullSafeToString<T>(T obj)
400400
{
401401
return obj == null ? "(null)" : obj.ToString();
402402
}
@@ -407,6 +407,11 @@ private static string NullSafeToString(object obj)
407407
/// <param name="array"></param>
408408
/// <returns></returns>
409409
public static string ToString(object[] array)
410+
{
411+
return ToString<object>(array);
412+
}
413+
414+
internal static string ToString<T>(T[] array)
410415
{
411416
int len = array.Length;
412417

0 commit comments

Comments
 (0)