Skip to content

Commit 7ccb678

Browse files
maca88fredericDelaporte
authored andcommitted
Refactor sequential select (#1979)
1 parent 116398d commit 7ccb678

File tree

7 files changed

+188
-174
lines changed

7 files changed

+188
-174
lines changed

src/NHibernate/Async/Loader/Loader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,7 @@ private async Task LoadFromResultSetAsync(DbDataReader rs, int i, object obj, st
907907
? EntityAliases[i].SuffixedPropertyAliases
908908
: GetSubclassEntityAliases(i, persister);
909909

910-
object[] values = await (persister.HydrateAsync(rs, id, obj, rootPersister, cols, eagerFetchProperties, eagerPropertyFetch, session, cancellationToken)).ConfigureAwait(false);
910+
object[] values = await (persister.HydrateAsync(rs, id, obj, cols, eagerFetchProperties, eagerPropertyFetch, session, cancellationToken)).ConfigureAwait(false);
911911

912912
object rowId = persister.HasRowId ? rs[EntityAliases[i].RowIdAlias] : null;
913913

src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs

Lines changed: 37 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
using Property=NHibernate.Mapping.Property;
3737
using NHibernate.SqlTypes;
3838
using System.Linq;
39+
using System.Linq.Expressions;
3940
using NHibernate.Bytecode;
4041

4142
namespace NHibernate.Persister.Entity
@@ -101,7 +102,7 @@ async Task InternalInitializeLazyPropertiesAsync()
101102
}
102103
}
103104

104-
var values = await (HydrateAsync(rs, id, entity, rootPersister, suffixedPropertyColumns, null, true, indexes, session, cancellationToken)).ConfigureAwait(false);
105+
var values = await (HydrateAsync(rs, id, entity, suffixedPropertyColumns, null, true, indexes, session, cancellationToken)).ConfigureAwait(false);
105106
for (var i = 0; i < lazyIndexes.Length; i++)
106107
{
107108
var value = values[i];
@@ -400,14 +401,14 @@ protected async Task<int> DehydrateAsync(object id, object[] fields, object rowI
400401
/// Unmarshall the fields of a persistent instance from a result set,
401402
/// without resolving associations or collections
402403
/// </summary>
403-
public Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, ILoadable rootLoadable,
404+
public Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
404405
string[][] suffixedPropertyColumns, ISet<string> fetchedLazyProperties, bool allProperties, ISessionImplementor session, CancellationToken cancellationToken)
405406
{
406407
if (cancellationToken.IsCancellationRequested)
407408
{
408409
return Task.FromCanceled<object[]>(cancellationToken);
409410
}
410-
return HydrateAsync(rs, id, obj, rootLoadable, suffixedPropertyColumns, fetchedLazyProperties, allProperties, null, session, cancellationToken);
411+
return HydrateAsync(rs, id, obj, suffixedPropertyColumns, fetchedLazyProperties, allProperties, null, session, cancellationToken);
411412
}
412413

413414
/// <summary>
@@ -423,14 +424,14 @@ public Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, ILoad
423424
{
424425
return Task.FromCanceled<object[]>(cancellationToken);
425426
}
426-
return HydrateAsync(rs, id, obj, rootLoadable, suffixedPropertyColumns, null, allProperties, null, session, cancellationToken);
427+
return HydrateAsync(rs, id, obj, suffixedPropertyColumns, null, allProperties, null, session, cancellationToken);
427428
}
428429

429430
/// <summary>
430431
/// Unmarshall the fields of a persistent instance from a result set,
431432
/// without resolving associations or collections
432433
/// </summary>
433-
private async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, ILoadable rootLoadable, string[][] suffixedPropertyColumns,
434+
private async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, string[][] suffixedPropertyColumns,
434435
ISet<string> fetchedLazyProperties, bool allProperties, int[] indexes, ISessionImplementor session, CancellationToken cancellationToken)
435436
{
436437
cancellationToken.ThrowIfCancellationRequested();
@@ -439,49 +440,43 @@ private async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj
439440
log.Debug("Hydrating entity: {0}", MessageHelper.InfoString(this, id, Factory));
440441
}
441442

442-
AbstractEntityPersister rootPersister = (AbstractEntityPersister)rootLoadable;
443-
444-
bool hasDeferred = rootPersister.HasSequentialSelect;
443+
var sequentialSql = GetSequentialSelect();
445444
DbCommand sequentialSelect = null;
446445
DbDataReader sequentialResultSet = null;
447446
bool sequentialSelectEmpty = false;
448447
using (session.BeginProcess())
449448
try
450449
{
451-
if (hasDeferred)
450+
if (sequentialSql != null)
452451
{
453-
SqlString sql = rootPersister.GetSequentialSelect(EntityName);
454-
if (sql != null)
452+
//TODO: I am not so sure about the exception handling in this bit!
453+
sequentialSelect = await (session.Batcher.PrepareCommandAsync(CommandType.Text, sequentialSql, IdentifierType.SqlTypes(factory), cancellationToken)).ConfigureAwait(false);
454+
await (IdentifierType.NullSafeSetAsync(sequentialSelect, id, 0, session, cancellationToken)).ConfigureAwait(false);
455+
sequentialResultSet = await (session.Batcher.ExecuteReaderAsync(sequentialSelect, cancellationToken)).ConfigureAwait(false);
456+
if (!await (sequentialResultSet.ReadAsync(cancellationToken)).ConfigureAwait(false))
455457
{
456-
//TODO: I am not so sure about the exception handling in this bit!
457-
sequentialSelect = await (session.Batcher.PrepareCommandAsync(CommandType.Text, sql, IdentifierType.SqlTypes(factory), cancellationToken)).ConfigureAwait(false);
458-
await (rootPersister.IdentifierType.NullSafeSetAsync(sequentialSelect, id, 0, session, cancellationToken)).ConfigureAwait(false);
459-
sequentialResultSet = await (session.Batcher.ExecuteReaderAsync(sequentialSelect, cancellationToken)).ConfigureAwait(false);
460-
if (!await (sequentialResultSet.ReadAsync(cancellationToken)).ConfigureAwait(false))
461-
{
462-
// TODO: Deal with the "optional" attribute in the <join> mapping;
463-
// this code assumes that optional defaults to "true" because it
464-
// doesn't actually seem to work in the fetch="join" code
465-
//
466-
// Note that actual proper handling of optional-ality here is actually
467-
// more involved than this patch assumes. Remember that we might have
468-
// multiple <join/> mappings associated with a single entity. Really
469-
// a couple of things need to happen to properly handle optional here:
470-
// 1) First and foremost, when handling multiple <join/>s, we really
471-
// should be using the entity root table as the driving table;
472-
// another option here would be to choose some non-optional joined
473-
// table to use as the driving table. In all likelihood, just using
474-
// the root table is much simplier
475-
// 2) Need to add the FK columns corresponding to each joined table
476-
// to the generated select list; these would then be used when
477-
// iterating the result set to determine whether all non-optional
478-
// data is present
479-
// My initial thoughts on the best way to deal with this would be
480-
// to introduce a new SequentialSelect abstraction that actually gets
481-
// generated in the persisters (ok, SingleTable...) and utilized here.
482-
// It would encapsulated all this required optional-ality checking...
483-
sequentialSelectEmpty = true;
484-
}
458+
// TODO: Deal with the "optional" attribute in the <join> mapping;
459+
// this code assumes that optional defaults to "true" because it
460+
// doesn't actually seem to work in the fetch="join" code
461+
//
462+
// Note that actual proper handling of optional-ality here is actually
463+
// more involved than this patch assumes. Remember that we might have
464+
// multiple <join/> mappings associated with a single entity. Really
465+
// a couple of things need to happen to properly handle optional here:
466+
// 1) First and foremost, when handling multiple <join/>s, we really
467+
// should be using the entity root table as the driving table;
468+
// another option here would be to choose some non-optional joined
469+
// table to use as the driving table. In all likelihood, just using
470+
// the root table is much simplier
471+
// 2) Need to add the FK columns corresponding to each joined table
472+
// to the generated select list; these would then be used when
473+
// iterating the result set to determine whether all non-optional
474+
// data is present
475+
// My initial thoughts on the best way to deal with this would be
476+
// to introduce a new SequentialSelect abstraction that actually gets
477+
// generated in the persisters (ok, SingleTable...) and utilized here.
478+
// It would encapsulated all this required optional-ality checking...
479+
sequentialSelectEmpty = true;
485480
}
486481
}
487482

@@ -491,7 +486,6 @@ private async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj
491486
IType[] types = PropertyTypes;
492487
object[] values = new object[length];
493488
bool[] laziness = PropertyLaziness;
494-
string[] propSubclassNames = SubclassPropertySubclassNameClosure;
495489

496490
for (int j = 0; j < length; j++)
497491
{
@@ -503,8 +497,7 @@ private async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj
503497
else if (allProperties || !laziness[i] || fetchedLazyProperties?.Contains(propNames[i]) == true)
504498
{
505499
//decide which ResultSet to get the property value from:
506-
bool propertyIsDeferred = hasDeferred
507-
&& rootPersister.IsSubclassPropertyDeferred(propNames[i], propSubclassNames[i]);
500+
var propertyIsDeferred = sequentialSql != null && IsPropertyDeferred(i);
508501
if (propertyIsDeferred && sequentialSelectEmpty)
509502
{
510503
values[j] = null;
@@ -522,15 +515,11 @@ private async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj
522515
}
523516
}
524517

525-
if (sequentialResultSet != null)
526-
{
527-
sequentialResultSet.Close();
528-
}
529-
530518
return values;
531519
}
532520
finally
533521
{
522+
sequentialResultSet?.Close();
534523
if (sequentialSelect != null)
535524
{
536525
session.Batcher.CloseCommand(sequentialSelect, sequentialResultSet);

src/NHibernate/Async/Persister/Entity/ILoadable.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public static partial class LoadableExtensions
3838
/// </summary>
3939
//6.0 TODO: Merge into ILoadable
4040
public static Task<object[]> HydrateAsync(
41-
this ILoadable loadable, DbDataReader rs, object id, object obj, ILoadable rootLoadable,
41+
this ILoadable loadable, DbDataReader rs, object id, object obj,
4242
string[][] suffixedPropertyColumns, ISet<string> fetchedLazyProperties, bool allProperties, ISessionImplementor session, CancellationToken cancellationToken)
4343
{
4444
if (cancellationToken.IsCancellationRequested)
@@ -50,8 +50,12 @@ public static Task<object[]> HydrateAsync(
5050
if (loadable is AbstractEntityPersister abstractEntityPersister)
5151
{
5252
return abstractEntityPersister.HydrateAsync(
53-
rs, id, obj, rootLoadable, suffixedPropertyColumns, fetchedLazyProperties, allProperties, session, cancellationToken);
53+
rs, id, obj, suffixedPropertyColumns, fetchedLazyProperties, allProperties, session, cancellationToken);
5454
}
55+
56+
var rootLoadable = loadable.RootEntityName == loadable.EntityName
57+
? loadable
58+
: (ILoadable) loadable.Factory.GetEntityPersister(loadable.RootEntityName);
5559

5660
#pragma warning disable 618
5761
// Fallback to the old behavior

src/NHibernate/Loader/Loader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1271,7 +1271,7 @@ private void LoadFromResultSet(DbDataReader rs, int i, object obj, string instan
12711271
? EntityAliases[i].SuffixedPropertyAliases
12721272
: GetSubclassEntityAliases(i, persister);
12731273

1274-
object[] values = persister.Hydrate(rs, id, obj, rootPersister, cols, eagerFetchProperties, eagerPropertyFetch, session);
1274+
object[] values = persister.Hydrate(rs, id, obj, cols, eagerFetchProperties, eagerPropertyFetch, session);
12751275

12761276
object rowId = persister.HasRowId ? rs[EntityAliases[i].RowIdAlias] : null;
12771277

0 commit comments

Comments
 (0)