Skip to content

Commit

Permalink
Fix SQL Server result mapping for bulk insert (#29565) (#29590)
Browse files Browse the repository at this point in the history
Fixes #29502

(cherry picked from commit a066241)
  • Loading branch information
roji committed Nov 29, 2022
1 parent 916e480 commit dd2c6c3
Show file tree
Hide file tree
Showing 7 changed files with 444 additions and 7 deletions.
Expand Up @@ -25,6 +25,9 @@ public class SqlServerModificationCommandBatch : AffectedCountModificationComman

private readonly List<IReadOnlyModificationCommand> _pendingBulkInsertCommands = new();

private static readonly bool QuirkEnabled29502
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29502", out var enabled) && enabled;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down Expand Up @@ -116,9 +119,21 @@ private void ApplyPendingBulkInsertCommands()
ResultSetMappings.Add(resultSetMapping);
}

if (resultSetMapping != ResultSetMapping.NoResults)
// All result mappings are marked as "not last", mark the last one as "last".
if (QuirkEnabled29502)
{
if (resultSetMapping != ResultSetMapping.NoResults)
{
ResultSetMappings[^1] = ResultSetMapping.LastInResultSet;
}
}
else
{
ResultSetMappings[^1] = ResultSetMapping.LastInResultSet;
if (resultSetMapping.HasFlag(ResultSetMapping.HasResultRow))
{
ResultSetMappings[^1] &= ~ResultSetMapping.NotLastInResultSet;
ResultSetMappings[^1] |= ResultSetMapping.LastInResultSet;
}
}
}

Expand Down
Expand Up @@ -12,8 +12,7 @@ protected override string StoreName
=> "NonSharedModelUpdatesTestBase";

[ConditionalTheory] // Issue #29356
[InlineData(false)]
[InlineData(true)]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Principal_and_dependent_roundtrips_with_cycle_breaking(bool async)
{
var contextFactory = await InitializeAsync<DbContext>(
Expand Down
Expand Up @@ -49,6 +49,9 @@
<None Update="SqlAzure\adventureworks.sql">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Update\Issue29502.sql">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
Expand Up @@ -29,8 +29,12 @@ public static SqlServerTestStore GetOrCreateInitialized(string name)
public static SqlServerTestStore GetOrCreateWithInitScript(string name, string initScript)
=> new(name, initScript: initScript);

public static SqlServerTestStore GetOrCreateWithScriptPath(string name, string scriptPath, bool? multipleActiveResultSets = null)
=> new(name, scriptPath: scriptPath, multipleActiveResultSets: multipleActiveResultSets);
public static SqlServerTestStore GetOrCreateWithScriptPath(
string name,
string scriptPath,
bool? multipleActiveResultSets = null,
bool shared = true)
=> new(name, scriptPath: scriptPath, multipleActiveResultSets: multipleActiveResultSets, shared: shared);

public static SqlServerTestStore Create(string name, bool useFileName = false)
=> new(name, useFileName, shared: false);
Expand Down
370 changes: 370 additions & 0 deletions test/EFCore.SqlServer.FunctionalTests/Update/Issue29502.sql

Large diffs are not rendered by default.

Expand Up @@ -84,6 +84,52 @@ OUTPUT 1
""");
}

[ConditionalFact] // Issue #29502
public virtual async Task Bulk_insert_result_set_mapping()
{
var contextFactory = await InitializeAsync<DbContext>(
onModelCreating: mb =>
{
mb.Entity<User>().ToTable("Users");
mb.Entity<DailyDigest>().ToTable("DailyDigests");
},
createTestStore: () => SqlServerTestStore.GetOrCreateWithScriptPath(
"Issue29502",
Path.Combine("Update", "Issue29502.sql"),
shared: false));

await ExecuteWithStrategyInTransactionAsync(
contextFactory,
async context =>
{
var digests = await context.Set<User>()
.OrderBy(u => u.TimeCreatedUtc)
.Take(23)
.Select(u => new DailyDigest { User = u })
.ToListAsync();
foreach (var digest in digests)
{
context.Set<DailyDigest>().Add(digest);
}
await context.SaveChangesAsync();
});
}

public class User
{
public string Id { get; set; } = null!;
public DateTime TimeCreatedUtc { get; set; }
public ICollection<DailyDigest> DailyDigests { get; set; } = null!;
}

public class DailyDigest
{
public int Id { get; set; }
public User User { get; set; }
}

private void AssertSql(params string[] expected)
=> TestSqlLoggerFactory.AssertBaseline(expected);

Expand Down
Expand Up @@ -3,7 +3,7 @@

namespace Microsoft.EntityFrameworkCore.Update;

public class NonSharedModelUpdatesSqlServerTest : NonSharedModelUpdatesTestBase
public class NonSharedModelUpdatesSqliteTest : NonSharedModelUpdatesTestBase
{
public override async Task Principal_and_dependent_roundtrips_with_cycle_breaking(bool async)
{
Expand Down

0 comments on commit dd2c6c3

Please sign in to comment.