Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/NHibernate/Async/Loader/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ private async Task LoadFromResultSetAsync(DbDataReader rs, int i, object obj, st
? EntityAliases[i].SuffixedPropertyAliases
: GetSubclassEntityAliases(i, persister);

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

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

Expand Down
94 changes: 49 additions & 45 deletions src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
using Property=NHibernate.Mapping.Property;
using NHibernate.SqlTypes;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Bytecode;

namespace NHibernate.Persister.Entity
Expand Down Expand Up @@ -339,7 +340,23 @@ protected async Task<int> DehydrateAsync(object id, object[] fields, object rowI
/// Unmarshall the fields of a persistent instance from a result set,
/// without resolving associations or collections
/// </summary>
public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, ILoadable rootLoadable,
//Since v5.3
[Obsolete("Use the overload without the rootLoadable parameter instead")]
public Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, ILoadable rootLoadable,
string[][] suffixedPropertyColumns, bool allProperties, ISessionImplementor session, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object[]>(cancellationToken);
}
return HydrateAsync(rs, id, obj, suffixedPropertyColumns, allProperties, session, cancellationToken);
}

/// <summary>
/// Unmarshall the fields of a persistent instance from a result set,
/// without resolving associations or collections
/// </summary>
public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
string[][] suffixedPropertyColumns, bool allProperties, ISessionImplementor session, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Expand All @@ -348,57 +365,49 @@ public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
log.Debug("Hydrating entity: {0}", MessageHelper.InfoString(this, id, Factory));
}

AbstractEntityPersister rootPersister = (AbstractEntityPersister)rootLoadable;

bool hasDeferred = rootPersister.HasSequentialSelect;
var sequentialSql = GetSequentialSelect();
DbCommand sequentialSelect = null;
DbDataReader sequentialResultSet = null;
bool sequentialSelectEmpty = false;
using (session.BeginProcess())
try
{
if (hasDeferred)
if (sequentialSql != null)
{
SqlString sql = rootPersister.GetSequentialSelect(EntityName);
if (sql != null)
//TODO: I am not so sure about the exception handling in this bit!
sequentialSelect = await (session.Batcher.PrepareCommandAsync(CommandType.Text, sequentialSql, IdentifierType.SqlTypes(factory), cancellationToken)).ConfigureAwait(false);
await (IdentifierType.NullSafeSetAsync(sequentialSelect, id, 0, session, cancellationToken)).ConfigureAwait(false);
sequentialResultSet = await (session.Batcher.ExecuteReaderAsync(sequentialSelect, cancellationToken)).ConfigureAwait(false);
if (!await (sequentialResultSet.ReadAsync(cancellationToken)).ConfigureAwait(false))
{
//TODO: I am not so sure about the exception handling in this bit!
sequentialSelect = await (session.Batcher.PrepareCommandAsync(CommandType.Text, sql, IdentifierType.SqlTypes(factory), cancellationToken)).ConfigureAwait(false);
await (rootPersister.IdentifierType.NullSafeSetAsync(sequentialSelect, id, 0, session, cancellationToken)).ConfigureAwait(false);
sequentialResultSet = await (session.Batcher.ExecuteReaderAsync(sequentialSelect, cancellationToken)).ConfigureAwait(false);
if (!await (sequentialResultSet.ReadAsync(cancellationToken)).ConfigureAwait(false))
{
// TODO: Deal with the "optional" attribute in the <join> mapping;
// this code assumes that optional defaults to "true" because it
// doesn't actually seem to work in the fetch="join" code
//
// Note that actual proper handling of optional-ality here is actually
// more involved than this patch assumes. Remember that we might have
// multiple <join/> mappings associated with a single entity. Really
// a couple of things need to happen to properly handle optional here:
// 1) First and foremost, when handling multiple <join/>s, we really
// should be using the entity root table as the driving table;
// another option here would be to choose some non-optional joined
// table to use as the driving table. In all likelihood, just using
// the root table is much simplier
// 2) Need to add the FK columns corresponding to each joined table
// to the generated select list; these would then be used when
// iterating the result set to determine whether all non-optional
// data is present
// My initial thoughts on the best way to deal with this would be
// to introduce a new SequentialSelect abstraction that actually gets
// generated in the persisters (ok, SingleTable...) and utilized here.
// It would encapsulated all this required optional-ality checking...
sequentialSelectEmpty = true;
}
// TODO: Deal with the "optional" attribute in the <join> mapping;
// this code assumes that optional defaults to "true" because it
// doesn't actually seem to work in the fetch="join" code
//
// Note that actual proper handling of optional-ality here is actually
// more involved than this patch assumes. Remember that we might have
// multiple <join/> mappings associated with a single entity. Really
// a couple of things need to happen to properly handle optional here:
// 1) First and foremost, when handling multiple <join/>s, we really
// should be using the entity root table as the driving table;
// another option here would be to choose some non-optional joined
// table to use as the driving table. In all likelihood, just using
// the root table is much simplier
// 2) Need to add the FK columns corresponding to each joined table
// to the generated select list; these would then be used when
// iterating the result set to determine whether all non-optional
// data is present
// My initial thoughts on the best way to deal with this would be
// to introduce a new SequentialSelect abstraction that actually gets
// generated in the persisters (ok, SingleTable...) and utilized here.
// It would encapsulated all this required optional-ality checking...
sequentialSelectEmpty = true;
}
}

string[] propNames = PropertyNames;
IType[] types = PropertyTypes;
object[] values = new object[types.Length];
bool[] laziness = PropertyLaziness;
string[] propSubclassNames = SubclassPropertySubclassNameClosure;

for (int i = 0; i < types.Length; i++)
{
Expand All @@ -409,8 +418,7 @@ public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
else if (allProperties || !laziness[i])
{
//decide which ResultSet to get the property value from:
bool propertyIsDeferred = hasDeferred
&& rootPersister.IsSubclassPropertyDeferred(propNames[i], propSubclassNames[i]);
var propertyIsDeferred = sequentialSql != null && IsPropertyDeferred(i);
if (propertyIsDeferred && sequentialSelectEmpty)
{
values[i] = null;
Expand All @@ -428,15 +436,11 @@ public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
}
}

if (sequentialResultSet != null)
{
sequentialResultSet.Close();
}

return values;
}
finally
{
sequentialResultSet?.Close();
if (sequentialSelect != null)
{
session.Batcher.CloseCommand(sequentialSelect, sequentialResultSet);
Expand Down
40 changes: 40 additions & 0 deletions src/NHibernate/Async/Persister/Entity/ILoadable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//------------------------------------------------------------------------------


using System;
using NHibernate.Type;
using NHibernate.Engine;
using System.Data.Common;
Expand All @@ -23,7 +24,46 @@ public partial interface ILoadable : IEntityPersister
/// <summary>
/// Retrieve property values from one row of a result set
/// </summary>
//Since v5.3
[Obsolete("Use the extension method without the rootLoadable parameter instead")]
Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, ILoadable rootLoadable, string[][] suffixedPropertyColumns,
bool allProperties, ISessionImplementor session, CancellationToken cancellationToken);
}

public static partial class LoadableExtensions
{
public static Task<object[]> HydrateAsync(
this ILoadable loadable,
DbDataReader rs,
object id,
object obj,
string[][] suffixedPropertyColumns,
bool allProperties,
ISessionImplementor session, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object[]>(cancellationToken);
}
try
{
if (loadable is AbstractEntityPersister entityPersister)
{
return entityPersister.HydrateAsync(rs, id, obj, suffixedPropertyColumns, allProperties, session, cancellationToken);
}

var rootLoadable = loadable.RootEntityName == loadable.EntityName
? loadable
: (ILoadable) loadable.Factory.GetEntityPersister(loadable.RootEntityName);

#pragma warning disable 618
return loadable.HydrateAsync(rs, id, obj, rootLoadable, suffixedPropertyColumns, allProperties, session, cancellationToken);
#pragma warning restore 618
}
catch (Exception ex)
{
return Task.FromException<object[]>(ex);
}
}
}
}
2 changes: 1 addition & 1 deletion src/NHibernate/Loader/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ private void LoadFromResultSet(DbDataReader rs, int i, object obj, string instan
? EntityAliases[i].SuffixedPropertyAliases
: GetSubclassEntityAliases(i, persister);

object[] values = persister.Hydrate(rs, id, obj, rootPersister, cols, eagerPropertyFetch, session);
object[] values = persister.Hydrate(rs, id, obj, cols, eagerPropertyFetch, session);

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

Expand Down
Loading