Skip to content

Commit

Permalink
Handle shadow navigations when loading with split queries
Browse files Browse the repository at this point in the history
Fixes #32225
  • Loading branch information
ajcvickers committed Nov 22, 2023
1 parent 1b59013 commit 6d37cfe
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ private static void InitializeSplitIncludeCollection<TParent, TNavigationEntity>
TParent entity,
Func<QueryContext, DbDataReader, object[]> parentIdentifier,
INavigationBase navigation,
IClrCollectionAccessor clrCollectionAccessor,
IClrCollectionAccessor? clrCollectionAccessor,
bool trackingQuery,
bool setLoaded)
where TParent : class
Expand All @@ -348,7 +348,7 @@ private static void InitializeSplitIncludeCollection<TParent, TNavigationEntity>
}
}

collection = clrCollectionAccessor.GetOrCreate(entity, forMaterialization: true);
collection = clrCollectionAccessor?.GetOrCreate(entity, forMaterialization: true);
}

var parentKey = parentIdentifier(queryContext, parentDataReader);
Expand Down
8 changes: 8 additions & 0 deletions src/EFCore/Metadata/Internal/ClrCollectionAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class ClrICollectionAccessor<TEntity, TCollection, TElement> : IClrCollec
where TElement : class
{
private readonly string _propertyName;
private readonly bool _shadow;
private readonly Func<TEntity, TCollection>? _getCollection;
private readonly Action<TEntity, TCollection>? _setCollection;
private readonly Action<TEntity, TCollection>? _setCollectionForMaterialization;
Expand All @@ -40,13 +41,15 @@ public virtual Type CollectionType
/// </summary>
public ClrICollectionAccessor(
string propertyName,
bool shadow,
Func<TEntity, TCollection>? getCollection,
Action<TEntity, TCollection>? setCollection,
Action<TEntity, TCollection>? setCollectionForMaterialization,
Func<TEntity, Action<TEntity, TCollection>, TCollection>? createAndSetCollection,
Func<TCollection>? createCollection)
{
_propertyName = propertyName;
_shadow = shadow;
_getCollection = getCollection;
_setCollection = setCollection;
_setCollectionForMaterialization = setCollectionForMaterialization;
Expand Down Expand Up @@ -137,6 +140,11 @@ private ICollection<TElement> GetOrCreateCollection(object instance, bool forMat

private ICollection<TElement>? GetCollection(object instance)
{
if (_shadow)
{
return (ICollection<TElement>?)_createCollection?.Invoke();
}

var enumerable = _getCollection!((TEntity)instance);
var collection = enumerable as ICollection<TElement>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ private static IClrCollectionAccessor CreateGeneric<TEntity, TCollection, TEleme

return new ClrICollectionAccessor<TEntity, TCollection, TElement>(
navigation.Name,
navigation.IsShadowProperty(),
getterDelegate,
setterDelegate,
setterDelegateForMaterialization,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

<ItemGroup>
<ProjectReference Include="..\..\src\EFCore\EFCore.csproj" />
<ProjectReference Include="..\..\src\EFCore.Analyzers\EFCore.Analyzers.csproj" />
<ProjectReference Include="..\..\src\EFCore.Design\EFCore.Design.csproj" />
<ProjectReference Include="..\..\src\EFCore.Proxies\EFCore.Proxies.csproj" />
<ProjectReference Include="..\..\src\EFCore.Abstractions\EFCore.Abstractions.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Sqlite.Internal;

namespace Microsoft.EntityFrameworkCore.Query;

public class ManyToManyNoTrackingSplitQuerySqliteTest
: ManyToManyNoTrackingQueryRelationalTestBase<ManyToManySplitQuerySqliteFixture>
{
public ManyToManyNoTrackingSplitQuerySqliteTest(ManyToManySplitQuerySqliteFixture fixture)
: base(fixture)
{
}

// Sqlite does not support Apply operations

public override async Task Skip_navigation_order_by_single_or_default(bool async)
=> Assert.Equal(
SqliteStrings.ApplyNotSupported,
(await Assert.ThrowsAsync<InvalidOperationException>(
() => base.Skip_navigation_order_by_single_or_default(async))).Message);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Query;

public class ManyToManySplitQuerySqliteFixture : ManyToManyQuerySqliteFixture
{
protected override string StoreName
=> "ManyToManySplitQuery";

public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
=> base.AddOptions(builder.UseSqlite(b => b.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Sqlite.Internal;

namespace Microsoft.EntityFrameworkCore.Query;

public class ManyToManySplitQuerySqliteTest : ManyToManyQueryRelationalTestBase<ManyToManySplitQuerySqliteFixture>
{
public ManyToManySplitQuerySqliteTest(ManyToManySplitQuerySqliteFixture fixture)
: base(fixture)
{
}

public override async Task Skip_navigation_order_by_single_or_default(bool async)
=> Assert.Equal(
SqliteStrings.ApplyNotSupported,
(await Assert.ThrowsAsync<InvalidOperationException>(
() => base.Skip_navigation_order_by_single_or_default(async))).Message);
}

0 comments on commit 6d37cfe

Please sign in to comment.