Skip to content

Commit 1ac0ad5

Browse files
committed
Fix NH-3350 (Duplicate records using Future())
1 parent 4432f97 commit 1ac0ad5

File tree

6 files changed

+68
-43
lines changed

6 files changed

+68
-43
lines changed

src/NHibernate.Test/Async/Futures/MultiAnyQuerytBatchFixture.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,31 @@ public async Task CanFetchCollectionInBatchAsync()
106106
}
107107
}
108108

109+
//NH-3350 (Duplicate records using Future())
110+
[Test]
111+
public async Task SameCollectionFetchesAsync()
112+
{
113+
using (var sqlLog = new SqlLogSpy())
114+
using (var session = OpenSession())
115+
{
116+
var entiyComplex = session.QueryOver<EntityComplex>().Where(c => c.Id == _parentId).FutureValue();
117+
118+
session.QueryOver<EntityComplex>()
119+
.Fetch(SelectMode.Fetch, ec => ec.ChildrenList)
120+
.Where(c => c.Id == _parentId).Future();
121+
122+
session.QueryOver<EntityComplex>()
123+
.Fetch(SelectMode.Fetch, ec => ec.ChildrenList)
124+
.Where(c => c.Id == _parentId).Future();
125+
126+
var parent = await (entiyComplex.GetValueAsync());
127+
Assert.That(NHibernateUtil.IsInitialized(parent), Is.True);
128+
Assert.That(NHibernateUtil.IsInitialized(parent.ChildrenList), Is.True);
129+
Assert.That(parent.ChildrenList.Count, Is.EqualTo(2));
130+
131+
}
132+
}
133+
109134
#region Temp tests for debugging
110135

111136
[Test, Explicit]

src/NHibernate.Test/Futures/MultiAnyQuerytBatchFixture.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,31 @@ public void CanFetchCollectionInBatch()
9595
}
9696
}
9797

98+
//NH-3350 (Duplicate records using Future())
99+
[Test]
100+
public void SameCollectionFetches()
101+
{
102+
using (var sqlLog = new SqlLogSpy())
103+
using (var session = OpenSession())
104+
{
105+
var entiyComplex = session.QueryOver<EntityComplex>().Where(c => c.Id == _parentId).FutureValue();
106+
107+
session.QueryOver<EntityComplex>()
108+
.Fetch(SelectMode.Fetch, ec => ec.ChildrenList)
109+
.Where(c => c.Id == _parentId).Future();
110+
111+
session.QueryOver<EntityComplex>()
112+
.Fetch(SelectMode.Fetch, ec => ec.ChildrenList)
113+
.Where(c => c.Id == _parentId).Future();
114+
115+
var parent = entiyComplex.Value;
116+
Assert.That(NHibernateUtil.IsInitialized(parent), Is.True);
117+
Assert.That(NHibernateUtil.IsInitialized(parent.ChildrenList), Is.True);
118+
Assert.That(parent.ChildrenList.Count, Is.EqualTo(2));
119+
120+
}
121+
}
122+
98123
#region Temp tests for debugging
99124

100125
[Test, Explicit]

src/NHibernate/Async/Multi/IQueryBatchItem.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ namespace NHibernate
2222
public partial interface IQueryBatchItem
2323
{
2424

25-
/// <summary>
26-
/// Executed after all commands are processed
27-
/// </summary>
28-
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
29-
Task PostProcessAsync(CancellationToken cancellationToken = default(CancellationToken));
30-
3125
/// <summary>
3226
/// Immediate query execution in case dialect is non batchable
3327
/// </summary>

src/NHibernate/Async/Multi/QueryBatch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public partial class QueryBatch : IQueryBatch
9898

9999
foreach (var multiSource in _queries)
100100
{
101-
await (multiSource.PostProcessAsync(cancellationToken)).ConfigureAwait(false);
101+
multiSource.PostProcess();
102102
}
103103
}
104104
catch (OperationCanceledException) { throw; }

src/NHibernate/Async/Multi/QueryBatchItemBase.cs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,6 @@ namespace NHibernate
2626
public abstract partial class QueryBatchItemBase<TResult> : IQueryBatchItem<TResult>
2727
{
2828

29-
public async Task PostProcessAsync(CancellationToken cancellationToken = default(CancellationToken))
30-
{
31-
cancellationToken.ThrowIfCancellationRequested();
32-
if (_reader == null)
33-
return;
34-
35-
for (int i = 0; i < _queryInfos.Count; i++)
36-
{
37-
Loader.Loader loader = _queryInfos[i].Loader;
38-
await (loader.InitializeEntitiesAndCollectionsAsync(
39-
_hydratedObjects[i], _reader, Session, Session.PersistenceContext.DefaultReadOnly, cancellationToken)).ConfigureAwait(false);
40-
41-
if (_subselectResultKeys[i] != null)
42-
{
43-
loader.CreateSubselects(_subselectResultKeys[i], _queryInfos[i].Parameters, Session);
44-
}
45-
46-
//Maybe put in cache...
47-
_queryInfos[i].PutInCacheAction?.Invoke(_loaderResults[i]);
48-
}
49-
_reader = null;
50-
}
51-
5229
public async Task ExecuteNonBatchableAsync(CancellationToken cancellationToken = default(CancellationToken))
5330
{
5431
cancellationToken.ThrowIfCancellationRequested();

src/NHibernate/Multi/QueryBatchItemBase.cs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@ namespace NHibernate
1717
public abstract partial class QueryBatchItemBase<TResult> : IQueryBatchItem<TResult>
1818
{
1919
protected ISessionImplementor Session;
20-
private List<object>[] _hydratedObjects;
2120
private List<EntityKey[]>[] _subselectResultKeys;
2221
private IList[] _loaderResults;
2322

2423
private List<QueryLoadInfo> _queryInfos;
25-
private DbDataReader _reader;
2624
private IList<TResult> _finalResults;
2725

2826
protected class QueryLoadInfo
@@ -45,7 +43,6 @@ public virtual void Init(ISessionImplementor session)
4543
_queryInfos = GetQueryLoadInfo();
4644

4745
var count = _queryInfos.Count;
48-
_hydratedObjects = new List<object>[count];
4946
_subselectResultKeys = new List<EntityKey[]>[count];
5047
_loaderResults = new IList[count];
5148
}
@@ -76,6 +73,7 @@ public IEnumerable<ISqlCommand> GetCommands()
7673
public IEnumerable<Func<DbDataReader, int>> GetProcessResultSetActions()
7774
{
7875
var dialect = Session.Factory.Dialect;
76+
List<object>[] hydratedObjects = new List<object>[_queryInfos.Count];
7977

8078
for (var i = 0; i < _queryInfos.Count; i++)
8179
{
@@ -90,7 +88,7 @@ public IEnumerable<Func<DbDataReader, int>> GetProcessResultSetActions()
9088
}
9189

9290
int entitySpan = loader.EntityPersisters.Length;
93-
_hydratedObjects[i] = entitySpan == 0 ? null : new List<object>(entitySpan);
91+
hydratedObjects[i] = entitySpan == 0 ? null : new List<object>(entitySpan);
9492
EntityKey[] keys = new EntityKey[entitySpan];
9593

9694
RowSelection selection = queryParameters.RowSelection;
@@ -103,7 +101,6 @@ public IEnumerable<Func<DbDataReader, int>> GetProcessResultSetActions()
103101
var index = i;
104102
yield return reader =>
105103
{
106-
_reader = reader;
107104
if (advanceSelection)
108105
{
109106
Loader.Loader.Advance(reader, selection);
@@ -130,7 +127,7 @@ public IEnumerable<Func<DbDataReader, int>> GetProcessResultSetActions()
130127
queryParameters,
131128
lockModeArray,
132129
optionalObjectKey,
133-
_hydratedObjects[index],
130+
hydratedObjects[index],
134131
keys,
135132
true,
136133
_queryInfos[index].CacheTransformer
@@ -144,22 +141,21 @@ public IEnumerable<Func<DbDataReader, int>> GetProcessResultSetActions()
144141
tmpResults.Add(o);
145142
}
146143
_loaderResults[index] = tmpResults;
144+
145+
if (index == _queryInfos.Count - 1)
146+
{
147+
InitializeEntitiesAndCollections(reader, hydratedObjects);
148+
}
147149
return rowCount;
148150
};
149151
}
150152
}
151153

152154
public void PostProcess()
153155
{
154-
if (_reader == null)
155-
return;
156-
157156
for (int i = 0; i < _queryInfos.Count; i++)
158157
{
159158
Loader.Loader loader = _queryInfos[i].Loader;
160-
loader.InitializeEntitiesAndCollections(
161-
_hydratedObjects[i], _reader, Session, Session.PersistenceContext.DefaultReadOnly);
162-
163159
if (_subselectResultKeys[i] != null)
164160
{
165161
loader.CreateSubselects(_subselectResultKeys[i], _queryInfos[i].Parameters, Session);
@@ -168,7 +164,6 @@ public void PostProcess()
168164
//Maybe put in cache...
169165
_queryInfos[i].PutInCacheAction?.Invoke(_loaderResults[i]);
170166
}
171-
_reader = null;
172167
}
173168

174169
public void ExecuteNonBatchable()
@@ -207,5 +202,14 @@ public IList<TResult> GetResults()
207202
}
208203

209204
protected abstract List<TResult> DoGetResults();
205+
206+
private void InitializeEntitiesAndCollections(DbDataReader reader, List<object>[] hydratedObjects)
207+
{
208+
for (int i = 0; i < _queryInfos.Count; i++)
209+
{
210+
_queryInfos[i].Loader.InitializeEntitiesAndCollections(
211+
hydratedObjects[i], reader, Session, Session.PersistenceContext.DefaultReadOnly);
212+
}
213+
}
210214
}
211215
}

0 commit comments

Comments
 (0)