Skip to content

Commit 7e15b29

Browse files
committed
Changes to fix the prefetch
1 parent 6f2fac6 commit 7e15b29

File tree

6 files changed

+175
-67
lines changed

6 files changed

+175
-67
lines changed

Orm/Xtensive.Orm/Orm/EntitySetBase.cs

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,22 @@ public abstract class EntitySetBase : SessionBound,
5757
/// <inheritdoc/>
5858
public FieldInfo Field { get; private set; }
5959

60+
/// <summary>
61+
/// Gets the number of elements contained in the <see cref="EntitySetBase"/>.
62+
/// </summary>
63+
public long Count
64+
{
65+
get {
66+
if (Owner.IsRemoved) {
67+
return 0; // WPF tries to use EntitySets of removed Entities
68+
}
69+
70+
EnsureIsLoaded(WellKnown.EntitySetPreloadCount);
71+
EnsureCountIsLoaded();
72+
return (long) State.TotalItemCount;
73+
}
74+
}
75+
6076
internal EntitySetState State { get; private set; }
6177

6278
/// <summary>
@@ -73,7 +89,7 @@ private IEnumerable<IEntity> InnerGetEntities()
7389
if (Owner.IsRemoved)
7490
yield break; // WPF tries to enumerate EntitySets of removed Entities
7591

76-
Prefetch();
92+
PrefetchInternal();
7793
foreach (var key in State) {
7894
var entity = Session.Query.SingleOrDefault(key);
7995
if (entity==null) {
@@ -103,21 +119,7 @@ public void Prefetch()
103119
public void Prefetch(int? maxItemCount)
104120
{
105121
EnsureOwnerIsNotRemoved();
106-
EnsureIsLoaded(maxItemCount);
107-
}
108-
109-
/// <summary>
110-
/// Gets the number of elements contained in the <see cref="EntitySetBase"/>.
111-
/// </summary>
112-
public long Count {
113-
get {
114-
if (Owner.IsRemoved)
115-
return 0; // WPF tries to use EntitySets of removed Entities
116-
117-
EnsureIsLoaded(WellKnown.EntitySetPreloadCount);
118-
EnsureCountIsLoaded();
119-
return (long) State.TotalItemCount;
120-
}
122+
EnsureIsLoaded(maxItemCount, State.ShouldUseForcePrefetch(null));
121123
}
122124

123125
/// <summary>
@@ -781,19 +783,29 @@ internal bool CheckStateIsLoaded()
781783
return false;
782784
}
783785

784-
private void EnsureIsLoaded(int? maxItemCount)
786+
private void PrefetchInternal()
787+
{
788+
EnsureOwnerIsNotRemoved();
789+
EnsureIsLoaded(null, false);
790+
}
791+
792+
private void EnsureIsLoaded(int? maxItemCount, bool ignoreFullyLoaded = false)
785793
{
786794
if (CheckStateIsLoaded()) {
787-
if (State.IsFullyLoaded)
795+
if (State.IsFullyLoaded && !ignoreFullyLoaded) {
788796
return;
797+
}
798+
State.TotalItemCount = null;
789799
var requestedItemCount = maxItemCount.HasValue
790800
? (int) maxItemCount
791801
: int.MaxValue;
792-
if (State.CachedItemCount > requestedItemCount)
802+
if (State.CachedItemCount > requestedItemCount) {
793803
return;
804+
}
794805
}
795-
using (Session.Activate())
806+
using (Session.Activate()) {
796807
Session.Handler.FetchEntitySet(Owner.Key, Field, maxItemCount);
808+
}
797809
}
798810

799811
private void EnsureCountIsLoaded()

Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Linq;
1111
using System.Threading;
1212
using Xtensive.Caching;
13+
using Xtensive.Core;
1314
using KeyCache = Xtensive.Caching.ICache<Xtensive.Orm.Key, Xtensive.Orm.Key>;
1415

1516
namespace Xtensive.Orm.Internals
@@ -40,8 +41,8 @@ public BackupedState(EntitySetState state)
4041
private readonly EntitySetBase owner;
4142
private readonly bool isDisconnected;
4243

44+
private Guid lastManualPrefetchId;
4345
private bool isLoaded;
44-
4546
private long? totalItemCount;
4647
private int version;
4748
private IDictionary<Key, Key> addedKeys;
@@ -134,10 +135,11 @@ public bool HasChanges
134135
/// <param name="keys">The keys.</param>
135136
public void Update(IEnumerable<Key> keys, long? count)
136137
{
137-
if (!isDisconnected)
138-
UpdateStateRegular(keys, count);
138+
139+
if (HasChanges)
140+
UpdateCachedState(keys, count);
139141
else
140-
UpdateStateDisconnected(keys, count);
142+
UpdateSyncedState(keys, count);
141143
}
142144

143145
/// <summary>
@@ -272,6 +274,34 @@ internal void RemapKeys(KeyMapping mapping)
272274
}
273275
}
274276

277+
internal bool ShouldUseForcePrefetch(Guid? currentPrefetchOperation)
278+
{
279+
if (currentPrefetchOperation.HasValue) {
280+
if (currentPrefetchOperation.Value == lastManualPrefetchId)
281+
return false;
282+
lastManualPrefetchId = currentPrefetchOperation.Value;
283+
}
284+
if (isDisconnected)
285+
return true;
286+
if (Session.Transaction != null)
287+
switch (Session.Transaction.Outermost.IsolationLevel) {
288+
case System.Transactions.IsolationLevel.ReadCommitted:
289+
case System.Transactions.IsolationLevel.ReadUncommitted:
290+
return true;
291+
case System.Transactions.IsolationLevel.RepeatableRead:
292+
return string.Equals(Session.Handlers.ProviderInfo.ProviderName, WellKnown.Provider.SqlServer, StringComparison.Ordinal);
293+
default:
294+
return false;
295+
}
296+
return false;
297+
}
298+
299+
internal void SetLastManualPrefetchId(Guid? prefetchOperationId)
300+
{
301+
if (prefetchOperationId.HasValue)
302+
lastManualPrefetchId = prefetchOperationId.Value;
303+
}
304+
275305
/// <inheritdoc/>
276306
protected override void Invalidate()
277307
{
@@ -323,7 +353,7 @@ IEnumerator IEnumerable.GetEnumerator()
323353
#endregion
324354

325355

326-
private void UpdateStateRegular(IEnumerable<Key> keys, long? count)
356+
private void UpdateSyncedState(IEnumerable<Key> keys, long? count)
327357
{
328358
FetchedKeys.Clear();
329359
TotalItemCount = count;
@@ -332,19 +362,20 @@ private void UpdateStateRegular(IEnumerable<Key> keys, long? count)
332362
Rebind();
333363
}
334364

335-
private void UpdateStateDisconnected(IEnumerable<Key> syncronizedKeys, long? count)
365+
public void UpdateCachedState(IEnumerable<Key> keys, long? count)
336366
{
337367
FetchedKeys.Clear();
338-
var countExceptRemoved = 0;
339-
foreach (var key in syncronizedKeys) {
368+
var becameRemovedOnSever = removedKeys.Keys.ToHashSet();
369+
TotalItemCount = count;
370+
foreach (var key in keys) {
371+
if (addedKeys.ContainsKey(key))
372+
addedKeys.Remove(key);
373+
else if (becameRemovedOnSever.Contains(key))
374+
becameRemovedOnSever.Remove(key);
340375
FetchedKeys.Add(key);
341-
if (!removedKeys.ContainsKey(key))
342-
countExceptRemoved++;
343376
}
344-
TotalItemCount = count.HasValue
345-
? countExceptRemoved + AddedItemsCount
346-
: count;
347-
Rebind();
377+
foreach (var removedOnServer in becameRemovedOnSever)
378+
removedKeys.Remove(removedOnServer);
348379
}
349380

350381
private void EnsureFetchedKeysIsNotNull()
@@ -379,6 +410,7 @@ internal EntitySetState(EntitySetBase entitySet)
379410
owner = entitySet;
380411
version = int.MinValue;
381412
isDisconnected = entitySet.Session.IsDisconnected;
413+
lastManualPrefetchId = Guid.Empty;
382414
}
383415
}
384416
}

Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,12 @@ public CacheKey(FieldInfo referencingField, int? itemCountLimit)
9191
public void RegisterQueryTask()
9292
{
9393
EntitySetState state;
94-
if (isOwnerCached && manager.Owner.LookupState(ownerKey, ReferencingField, out state))
95-
if (state==null || state.IsFullyLoaded)
94+
if (isOwnerCached && manager.Owner.LookupState(ownerKey, ReferencingField, out state)) {
95+
if (state == null || (state.IsFullyLoaded && !state.ShouldUseForcePrefetch(referencingFieldDescriptor.PrefetchOperationId))) {
9696
return;
97+
}
98+
}
99+
97100
itemsQueryTask = CreateQueryTask();
98101
manager.Owner.Session.RegisterInternalDelayedQuery(itemsQueryTask);
99102
}
@@ -136,8 +139,11 @@ public void UpdateCache()
136139
}
137140
}
138141
}
139-
manager.Owner.UpdateState(ownerKey, ReferencingField,
140-
ItemCountLimit==null || entityKeys.Count < ItemCountLimit, entityKeys, auxEntities);
142+
var updatedState = manager.Owner.UpdateState(ownerKey, ReferencingField,
143+
ItemCountLimit == null || entityKeys.Count < ItemCountLimit, entityKeys, auxEntities);
144+
if (updatedState != null) {
145+
updatedState.SetLastManualPrefetchId(referencingFieldDescriptor.PrefetchOperationId);
146+
}
141147
}
142148

143149
public bool Equals(EntitySetTask other)

Orm/Xtensive.Orm/Orm/Internals/Prefetch/Fetcher.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,10 @@ private static void UpdateCacheFromAllEntitySetTasks(IEnumerable<GraphContainer>
7171

7272
private static void RegisterAllEntitySetTasks(IEnumerable<GraphContainer> containers)
7373
{
74-
foreach (var container in containers) {
74+
foreach (var container in containers.Where(c => c.EntitySetTasks != null)) {
7575
var entitySetPrefetchTasks = container.EntitySetTasks;
76-
if (entitySetPrefetchTasks!=null) {
77-
foreach (var entitySetPrefetchTask in entitySetPrefetchTasks)
78-
entitySetPrefetchTask.RegisterQueryTask();
76+
foreach (var entitySetPrefetchTask in entitySetPrefetchTasks) {
77+
entitySetPrefetchTask.RegisterQueryTask();
7978
}
8079
}
8180
}

Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchFacade.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,26 +44,27 @@ public IEnumerator<T> GetEnumerator()
4444
var aggregatedNodes = NodeAggregator<T>.Aggregate(nodes);
4545
var resultQueue = new Queue<T>();
4646
var container = new StrongReferenceContainer(null);
47+
var enumerationIdentifier = Guid.NewGuid();
4748
foreach (var item in source) {
4849
resultQueue.Enqueue(item);
4950
if (item!=null)
5051
foreach (var extractorNode in aggregatedNodes)
51-
container.JoinIfPossible(RegisterPrefetch(extractorNode.ExtractKeys(item), extractorNode));
52+
container.JoinIfPossible(RegisterPrefetch(extractorNode.ExtractKeys(item), extractorNode, enumerationIdentifier));
5253
if (currentTaskCount==sessionHandler.PrefetchTaskExecutionCount)
5354
continue;
5455
while (container.JoinIfPossible(sessionHandler.ExecutePrefetchTasks()))
55-
container.JoinIfPossible(ProcessFetchedElements());
56+
container.JoinIfPossible(ProcessFetchedElements(enumerationIdentifier));
5657
while (resultQueue.Count > 0)
5758
yield return resultQueue.Dequeue();
5859
currentTaskCount = sessionHandler.PrefetchTaskExecutionCount;
5960
}
6061
while (container.JoinIfPossible(sessionHandler.ExecutePrefetchTasks()))
61-
container.JoinIfPossible(ProcessFetchedElements());
62+
container.JoinIfPossible(ProcessFetchedElements(enumerationIdentifier));
6263
while (resultQueue.Count > 0)
6364
yield return resultQueue.Dequeue();
6465
}
6566

66-
private StrongReferenceContainer RegisterPrefetch(IEnumerable<Key> keys, IHasNestedNodes fieldContainer)
67+
private StrongReferenceContainer RegisterPrefetch(IEnumerable<Key> keys, IHasNestedNodes fieldContainer, Guid enumerationId)
6768
{
6869
var container = new StrongReferenceContainer(null);
6970
TypeInfo modelType = null;
@@ -81,10 +82,10 @@ private StrongReferenceContainer RegisterPrefetch(IEnumerable<Key> keys, IHasNes
8182
if (!fieldDescriptorCache.TryGetValue(cacheKey, out fieldDescriptors)) {
8283
fieldDescriptors = PrefetchHelper
8384
.GetCachedDescriptorsForFieldsLoadedByDefault(session.Domain, type)
84-
.Concat(fieldContainer.NestedNodes.Select(fn => new PrefetchFieldDescriptor(fn.Field, false, true))).ToList();
85+
.Concat(fieldContainer.NestedNodes.Select(fn => new PrefetchFieldDescriptor(fn.Field, false, true, enumerationId))).ToList();
8586
fieldDescriptorCache.Add(cacheKey, fieldDescriptors);
8687
}
87-
container.JoinIfPossible(sessionHandler.Prefetch(key, type, fieldDescriptors));
88+
_ = container.JoinIfPossible(sessionHandler.Prefetch(key, type, fieldDescriptors));
8889
}
8990
var nestedContainers = fieldContainer.NestedNodes.OfType<IHasNestedNodes>();
9091
foreach (var nestedContainer in nestedContainers)
@@ -93,7 +94,7 @@ private StrongReferenceContainer RegisterPrefetch(IEnumerable<Key> keys, IHasNes
9394
return container;
9495
}
9596

96-
private StrongReferenceContainer ProcessFetchedElements()
97+
private StrongReferenceContainer ProcessFetchedElements(Guid enumerationId)
9798
{
9899
var container = new StrongReferenceContainer(null);
99100
taskCount = sessionHandler.PrefetchTaskExecutionCount;
@@ -119,7 +120,7 @@ private StrongReferenceContainer ProcessFetchedElements()
119120
}
120121
keys.AddRange(nestedNodes.ExtractKeys(entityState.Entity));
121122
}
122-
container.JoinIfPossible(RegisterPrefetch(keys, nestedNodes));
123+
container.JoinIfPossible(RegisterPrefetch(keys, nestedNodes, enumerationId));
123124
}
124125
return container;
125126
}

0 commit comments

Comments
 (0)