Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions src/EfLocalDb.MSTest/LocalDbTestBase_Verify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ QueryableSettingsTask<TEntity> InnerVerifyEntity<TEntity>(object id, string sour
var expression = Expression.Lambda<Func<TEntity, bool>>(predicate, parameter);

return new(
set.AsSplitQuery(),
set,
null,
async (settings, source) =>
{
using var verifier = BuildVerifier(settings, sourceFile);
return await verifier.Verify(source.SingleOrDefaultAsync(expression));
return await verifier.Verify(QuerySplitting.SplitIfIncludesCollection(source).SingleOrDefaultAsync(expression));
});
}
}
68 changes: 68 additions & 0 deletions src/EfLocalDb.MSTest/QueryableSettingsTask/QuerySplitting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Collections;

namespace EfLocalDb;

// AsSplitQuery only changes how collections are loaded. So only apply it when an Include loads a collection,
// since a query that loads no collection is a single query either way (and Verify.EntityFramework throws for it).
static class QuerySplitting
{
public static IQueryable<TEntity> SplitIfIncludesCollection<TEntity>(IQueryable<TEntity> source)
where TEntity : class
{
if (IncludesCollection(source.Expression))
{
return source.AsSplitQuery();
}

return source;
}

static bool IncludesCollection(Expression expression)
{
while (expression is MethodCallExpression
{
Method.IsStatic: true,
Arguments.Count: > 0
} call)
{
if (IsInclude(call.Method) &&
call.Arguments.Count > 1)
{
var argument = Unquote(call.Arguments[1]);
// a string Include can not be checked without resolving the path, so it is assumed to be a collection
if (argument is not LambdaExpression lambda ||
IsCollection(lambda.Body.Type))
{
return true;
}
}

expression = call.Arguments[0];
}

return false;
}

static Expression Unquote(Expression expression)
{
while (expression is UnaryExpression
{
NodeType: ExpressionType.Quote
} unary)
{
expression = unary.Operand;
}

return expression;
}

static bool IsInclude(MethodInfo method) =>
method.DeclaringType == typeof(EntityFrameworkQueryableExtensions) &&
method.Name is
nameof(EntityFrameworkQueryableExtensions.Include) or
nameof(EntityFrameworkQueryableExtensions.ThenInclude);

static bool IsCollection(Type type) =>
type != typeof(string) &&
typeof(IEnumerable).IsAssignableFrom(type);
}
1 change: 1 addition & 0 deletions src/EfLocalDb.NUnit/EfLocalDb.NUnit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Compile Include="..\EfLocalDb.MSTest\SharedDbWithTransactionAttribute.cs" Link="SharedDbWithTransactionAttribute.cs" />
<Compile Include="..\EfLocalDb.MSTest\QueryableSettingsTask\QueryableSettingsTask.cs" Link="QueryableSettingsTask\QueryableSettingsTask.cs" />
<Compile Include="..\EfLocalDb.MSTest\QueryableSettingsTask\NestedQueryableSettingsTask.cs" Link="QueryableSettingsTask\NestedQueryableSettingsTask.cs" />
<Compile Include="..\EfLocalDb.MSTest\QueryableSettingsTask\QuerySplitting.cs" Link="QueryableSettingsTask\QuerySplitting.cs" />
<PackageReference Include="Verify.EntityFramework" />
<PackageReference Include="Verify.NUnit" />
<PackageReference Include="Argon" />
Expand Down
4 changes: 2 additions & 2 deletions src/EfLocalDb.NUnit/LocalDbTestBase_Verify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ QueryableSettingsTask<TEntity> InnerVerifyEntity<TEntity>(object id, string sour
var expression = Expression.Lambda<Func<TEntity, bool>>(predicate, parameter);

return new(
set.AsSplitQuery(),
set,
null,
async (settings, source) =>
{
using var verifier = BuildVerifier(sourceFile, settings);
return await verifier.Verify(source.SingleOrDefaultAsync(expression));
return await verifier.Verify(QuerySplitting.SplitIfIncludesCollection(source).SingleOrDefaultAsync(expression));
});
}
}
1 change: 1 addition & 0 deletions src/EfLocalDb.TUnit/EfLocalDb.TUnit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Compile Include="..\EfLocalDb.MSTest\SharedDbWithTransactionAttribute.cs" Link="SharedDbWithTransactionAttribute.cs" />
<Compile Include="..\EfLocalDb.MSTest\QueryableSettingsTask\QueryableSettingsTask.cs" Link="QueryableSettingsTask\QueryableSettingsTask.cs" />
<Compile Include="..\EfLocalDb.MSTest\QueryableSettingsTask\NestedQueryableSettingsTask.cs" Link="QueryableSettingsTask\NestedQueryableSettingsTask.cs" />
<Compile Include="..\EfLocalDb.MSTest\QueryableSettingsTask\QuerySplitting.cs" Link="QueryableSettingsTask\QuerySplitting.cs" />
<PackageReference Include="Verify.EntityFramework" />
<PackageReference Include="TUnit" />
<PackageReference Include="Verify.TUnit" />
Expand Down
4 changes: 2 additions & 2 deletions src/EfLocalDb.TUnit/LocalDbTestBase_Verify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ QueryableSettingsTask<TEntity> InnerVerifyEntity<TEntity>(object id, string sour
var expression = Expression.Lambda<Func<TEntity, bool>>(predicate, parameter);

return new(
set.AsSplitQuery(),
set,
null,
async (settings, source) =>
{
using var verifier = BuildVerifier(sourceFile, settings);
return await verifier.Verify(source.SingleOrDefaultAsync(expression));
return await verifier.Verify(QuerySplitting.SplitIfIncludesCollection(source).SingleOrDefaultAsync(expression));
});
}
}
1 change: 1 addition & 0 deletions src/EfLocalDb.Xunit.V3/EfLocalDb.Xunit.V3.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Compile Include="..\EfLocalDb.MSTest\SharedDbWithTransactionAttribute.cs" Link="SharedDbWithTransactionAttribute.cs" />
<Compile Include="..\EfLocalDb.MSTest\QueryableSettingsTask\QueryableSettingsTask.cs" Link="QueryableSettingsTask\QueryableSettingsTask.cs" />
<Compile Include="..\EfLocalDb.MSTest\QueryableSettingsTask\NestedQueryableSettingsTask.cs" Link="QueryableSettingsTask\NestedQueryableSettingsTask.cs" />
<Compile Include="..\EfLocalDb.MSTest\QueryableSettingsTask\QuerySplitting.cs" Link="QueryableSettingsTask\QuerySplitting.cs" />
<PackageReference Include="Verify.EntityFramework" />
<PackageReference Include="Verify.XunitV3" />
<PackageReference Include="Argon" />
Expand Down
4 changes: 2 additions & 2 deletions src/EfLocalDb.Xunit.V3/LocalDbTestBase_Verify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ QueryableSettingsTask<TEntity> InnerVerifyEntity<TEntity>(object id, string sour
var expression = Expression.Lambda<Func<TEntity, bool>>(predicate, parameter);

return new(
set.AsSplitQuery(),
set,
null,
async (settings, source) =>
{
using var verifier = BuildVerifier(settings, sourceFile);
return await verifier.Verify(source.SingleOrDefaultAsync(expression));
return await verifier.Verify(QuerySplitting.SplitIfIncludesCollection(source).SingleOrDefaultAsync(expression));
});
}
}
Loading