Skip to content

Commit fe0aa1b

Browse files
committed
WIP Optimize GetOrphans and remove wrong checks from IsNotTransientSlow
1 parent 8d13f24 commit fe0aa1b

File tree

7 files changed

+74
-52
lines changed

7 files changed

+74
-52
lines changed

src/NHibernate/Async/Collection/AbstractPersistentCollection.cs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using System.Collections;
1313
using System.Collections.Generic;
1414
using System.Data.Common;
15+
using System.Linq;
1516
using NHibernate.Collection.Generic;
1617
using NHibernate.Engine;
1718
using NHibernate.Impl;
@@ -174,29 +175,34 @@ protected virtual async Task<ICollection> GetOrphansAsync(ICollection oldElement
174175
return oldElements;
175176
}
176177

177-
IType idType = session.Factory.GetEntityPersister(entityName).IdentifierType;
178+
if (currentElements.Cast<object>().SequenceEqual(oldElements.Cast<object>()))
179+
return Array.Empty<object>();
178180

179-
// create the collection holding the orphans
180-
List<object> res = new List<object>();
181+
var persister = session.Factory.GetEntityPersister(entityName);
182+
IType idType = persister.IdentifierType;
181183

182-
// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
183-
var currentIds = new HashSet<TypedValue>();
184+
var currentObjects = new HashSet<object>(idType.GetComparer());
184185
foreach (object current in currentElements)
185186
{
186-
if (current != null && await (ForeignKeys.IsNotTransientSlowAsync(entityName, current, session, cancellationToken)).ConfigureAwait(false))
187+
if (current != null)
187188
{
188-
object currentId = await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(entityName, current, session, cancellationToken)).ConfigureAwait(false);
189-
currentIds.Add(new TypedValue(idType, currentId, false));
189+
var id = persister.GetIdentifier(current);
190+
if (id != null)
191+
currentObjects.Add(id);
190192
}
191193
}
192194

193-
// iterate over the *old* list
195+
List<object> res = new List<object>();
196+
// oldElements may contain new elements in some cases
194197
foreach (object old in oldElements)
195198
{
196-
object oldId = await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(entityName, old, session, cancellationToken)).ConfigureAwait(false);
197-
if (!currentIds.Contains(new TypedValue(idType, oldId, false)))
199+
var id = persister.GetIdentifier(old);
200+
if (id != null)
198201
{
199-
res.Add(old);
202+
if (!currentObjects.Contains(id) && await (ForeignKeys.IsNotTransientSlowAsync(persister.EntityName, old, Session, cancellationToken)).ConfigureAwait(false))
203+
{
204+
res.Add(id);
205+
}
200206
}
201207
}
202208

src/NHibernate/Async/Engine/ForeignKeys.cs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,6 @@ private async Task<bool> IsNullifiableAsync(string entityName, object obj, Cance
157157
public static async Task<bool> IsNotTransientSlowAsync(string entityName, object entity, ISessionImplementor session, CancellationToken cancellationToken)
158158
{
159159
cancellationToken.ThrowIfCancellationRequested();
160-
if (entity.IsProxy())
161-
return true;
162-
if (session.PersistenceContext.IsEntryFor(entity))
163-
return true;
164160
return !await (IsTransientSlowAsync(entityName, entity, session, cancellationToken)).ConfigureAwait(false);
165161
}
166162

@@ -174,6 +170,9 @@ public static async Task<bool> IsNotTransientSlowAsync(string entityName, object
174170
public static async Task<bool?> IsTransientFastAsync(string entityName, object entity, ISessionImplementor session, CancellationToken cancellationToken)
175171
{
176172
cancellationToken.ThrowIfCancellationRequested();
173+
if (entity.IsProxy())
174+
return false;
175+
177176
if (Equals(Intercept.LazyPropertyInitializer.UnfetchedProperty, entity))
178177
{
179178
// an unfetched association can only point to
@@ -252,16 +251,6 @@ public static async Task<object> GetEntityIdentifierIfNotUnsavedAsync(string ent
252251

253252
if ((await (IsTransientFastAsync(entityName, entity, session, cancellationToken)).ConfigureAwait(false)).GetValueOrDefault())
254253
{
255-
/***********************************************/
256-
// TODO NH verify the behavior of NH607 test
257-
// these lines are only to pass test NH607 during PersistenceContext porting
258-
// i'm not secure that NH607 is a test for a right behavior
259-
EntityEntry entry = session.PersistenceContext.GetEntry(entity);
260-
if (entry != null)
261-
return entry.Id;
262-
// the check was put here to have les possible impact
263-
/**********************************************/
264-
265254
entityName = entityName ?? session.GuessEntityName(entity);
266255
string entityString = entity.ToString();
267256
throw new TransientObjectException(

src/NHibernate/Async/Type/IType.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010

1111
using System.Collections;
12+
using System.Collections.Generic;
1213
using System.Data.Common;
1314
using NHibernate.Engine;
1415
using NHibernate.SqlTypes;

src/NHibernate/Collection/AbstractPersistentCollection.cs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.Data.Common;
5+
using System.Linq;
56
using NHibernate.Collection.Generic;
67
using NHibernate.Engine;
78
using NHibernate.Impl;
@@ -716,29 +717,34 @@ protected virtual ICollection GetOrphans(ICollection oldElements, ICollection cu
716717
return oldElements;
717718
}
718719

719-
IType idType = session.Factory.GetEntityPersister(entityName).IdentifierType;
720+
if (currentElements.Cast<object>().SequenceEqual(oldElements.Cast<object>()))
721+
return Array.Empty<object>();
720722

721-
// create the collection holding the orphans
722-
List<object> res = new List<object>();
723+
var persister = session.Factory.GetEntityPersister(entityName);
724+
IType idType = persister.IdentifierType;
723725

724-
// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
725-
var currentIds = new HashSet<TypedValue>();
726+
var currentObjects = new HashSet<object>(idType.GetComparer());
726727
foreach (object current in currentElements)
727728
{
728-
if (current != null && ForeignKeys.IsNotTransientSlow(entityName, current, session))
729+
if (current != null)
729730
{
730-
object currentId = ForeignKeys.GetEntityIdentifierIfNotUnsaved(entityName, current, session);
731-
currentIds.Add(new TypedValue(idType, currentId, false));
731+
var id = persister.GetIdentifier(current);
732+
if (id != null)
733+
currentObjects.Add(id);
732734
}
733735
}
734736

735-
// iterate over the *old* list
737+
List<object> res = new List<object>();
738+
// oldElements may contain new elements in some cases
736739
foreach (object old in oldElements)
737740
{
738-
object oldId = ForeignKeys.GetEntityIdentifierIfNotUnsaved(entityName, old, session);
739-
if (!currentIds.Contains(new TypedValue(idType, oldId, false)))
741+
var id = persister.GetIdentifier(old);
742+
if (id != null)
740743
{
741-
res.Add(old);
744+
if (!currentObjects.Contains(id) && ForeignKeys.IsNotTransientSlow(persister.EntityName, old, Session))
745+
{
746+
res.Add(id);
747+
}
742748
}
743749
}
744750

src/NHibernate/Engine/ForeignKeys.cs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,6 @@ private bool IsNullifiable(string entityName, object obj)
155155
/// </remarks>
156156
public static bool IsNotTransientSlow(string entityName, object entity, ISessionImplementor session)
157157
{
158-
if (entity.IsProxy())
159-
return true;
160-
if (session.PersistenceContext.IsEntryFor(entity))
161-
return true;
162158
return !IsTransientSlow(entityName, entity, session);
163159
}
164160

@@ -171,6 +167,9 @@ public static bool IsNotTransientSlow(string entityName, object entity, ISession
171167
/// </remarks>
172168
public static bool? IsTransientFast(string entityName, object entity, ISessionImplementor session)
173169
{
170+
if (entity.IsProxy())
171+
return false;
172+
174173
if (Equals(Intercept.LazyPropertyInitializer.UnfetchedProperty, entity))
175174
{
176175
// an unfetched association can only point to
@@ -246,16 +245,6 @@ public static object GetEntityIdentifierIfNotUnsaved(string entityName, object e
246245

247246
if (IsTransientFast(entityName, entity, session).GetValueOrDefault())
248247
{
249-
/***********************************************/
250-
// TODO NH verify the behavior of NH607 test
251-
// these lines are only to pass test NH607 during PersistenceContext porting
252-
// i'm not secure that NH607 is a test for a right behavior
253-
EntityEntry entry = session.PersistenceContext.GetEntry(entity);
254-
if (entry != null)
255-
return entry.Id;
256-
// the check was put here to have les possible impact
257-
/**********************************************/
258-
259248
entityName = entityName ?? session.GuessEntityName(entity);
260249
string entityString = entity.ToString();
261250
throw new TransientObjectException(

src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ public object Instantiate(object id)
116116

117117
public object GetIdentifier(object entity)
118118
{
119+
if (entity is INHibernateProxy proxy)
120+
return proxy.HibernateLazyInitializer.Identifier;
121+
119122
object id;
120123
if (entityMetamodel.IdentifierProperty.IsEmbedded)
121124
{

src/NHibernate/Type/IType.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,38 @@
11
using System.Collections;
2+
using System.Collections.Generic;
23
using System.Data.Common;
34
using NHibernate.Engine;
45
using NHibernate.SqlTypes;
56

67
namespace NHibernate.Type
78
{
9+
internal static class TypeExtensions
10+
{
11+
class TypeComparer : IEqualityComparer<object>
12+
{
13+
private readonly IType _type;
14+
15+
public TypeComparer(IType type)
16+
{
17+
_type = type;
18+
}
19+
20+
public new bool Equals(object x, object y)
21+
{
22+
return _type.IsEqual(x, y);
23+
}
24+
25+
public int GetHashCode(object obj)
26+
{
27+
return _type.GetHashCode(obj);
28+
}
29+
}
30+
31+
public static IEqualityComparer<object> GetComparer(this IType type)
32+
{
33+
return new TypeComparer(type);
34+
}
35+
}
836
/// <summary>
937
/// Defines a mapping from a .NET <see cref="System.Type"/> to a SQL data-type.
1038
/// This interface is intended to be implemented by applications that need custom types.

0 commit comments

Comments
 (0)