linq2db.EntityFrameworkCore
linq2db.EntityFrameworkCore copied to clipboard
`Merge()` fails with `StackOverflowException` when `OnTargetKey()` is being used
I might be doing something not as it is designed but I've spent more than 1 day to finally find out the reason.
What I'm trying to do is to execute a SQL MERGE using EF Core 5 on MS SQL server.
I start merge by using Merge() method.
- When I use
OnTargetKey()method to define merge condition then query fails withStackOverflowException - When I use
On()and manually define condition by matching source and target primary key then everything goes fine.
Packages in use:
Microsoft.EntityFrameworkCore.SqlServer 5.0.14
linq2db.EntityFrameworkCore 5.9.0
P.S. I'm using LinqPad 6.15 to test this MERGE.
Source code
async Task Main()
{
LinqToDB.Data.DataConnection.TurnTraceSwitchOn();
LinqToDB.Data.DataConnection.WriteTraceLine = (s1, s2, l) =>
{
if (l <= TraceLevel.Info) Util.SqlOutputWriter.WriteLine(s1);
};
var dbOptions = new DbContextOptionsBuilder<DataContext>()
.EnableSensitiveDataLogging()
.UseSqlServer("Data Source=(LocalDb)\\MSSQLLocalDB;Initial Catalog=xxxxx;Integrated Security=True;")
.Options;
using var db = new DataContext(dbOptions);
var id = 1;
var systemId = "system";
var ids = new List<int>() { id };
var result = new List<object>();
var resultEnum = db.Set<Account>()
.Where(x => ids.Contains(x.Id))
.Join(db.Set<AccountRevision>().Where(x => x.SystemId == systemId), x => x.Id, x => x.Id, (x, y) => y)
.ToLinqToDB()
.MergeInto(db.Set<AccountRevision>())
.OnTargetKey() // Fails with StackOverfloshExcpetion
//.On(x => new { x.Id, x.SystemId }, x => new { x.Id, x.SystemId }) // MERGE is successful
.UpdateWhenMatched()
.InsertWhenNotMatched()
.MergeWithOutputAsync((s, x, y) => new { action = s, y.Id });
await foreach (var item in resultEnum)
{
result.Add(item);
}
result.Dump("Merge result");
}
private class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AccountRevision>(builder =>
{
builder.ToTable("AccountRevisions", "test");
builder.HasKey(x => new { x.Id, x.SystemId });
builder.Property(x => x.SystemId)
.IsRequired(true)
.HasMaxLength(20)
.IsUnicode(false);
});
modelBuilder.Entity<Account>(builder =>
{
builder.ToTable("Accounts", "test");
builder.HasKey(x => x.Id);
});
}
}
public class Account
{
public int Id { get; set; }
}
public class AccountRevision
{
public int Id { get; set; }
public string SystemId {get; set;}
public DateTime Timestamp {get; set;}
}
The DB schema is
if not exists(select * from sys.schemas where name = N'test')
exec(N'create schema test authorization dbo')
go
create table test.Accounts(
Id int not null,
constraint [PK_test.Accounts] primary key (Id)
)
create table test.AccountRevisions(
Id int not null,
SystemId varchar(20) not null,
[Timestamp] datetime2 not null
constraint [PK_test.AccountRevisions] primary key (Id, SystemId)
)
drop table if exists test.AccountRevisions
drop table if exists test.Accounts
drop schema if exists test