优化了子级的插入与父级的合并

问题描述 投票:0回答:1

我有以下表格:

[ParentTable]
(1..m)
[ChildTable]
。两者都有
[Id] INT IDENTITY (1, 1)

我想确定使用实体框架在

[ChildTable]
中插入记录的最佳方式,而不需要在内存中获取
[ParentTable]
。更多:插入到
[ParentTable]
时,
[ChildTable]
中很多没有对应的记录。理想情况下,我们会为
MERGE
做一个
[ParentTable]
,并为
INSERT
做一个
[ChildTable]

使用 T-SQL,这将如下所示:

BEGIN TRANSACTION

DECLARE @InsertedIDs TABLE (ID INT);

MERGE TOP (1) INTO dbo.ParentTable as Target
USING 
(
  VALUES ('00000000-9F5D-4D3C-9EDC-65BCBEA2D88F', 'Some Description')
) AS Source (TraceId, Description) 
ON Target.TraceId = Source.TraceId
WHEN NOT MATCHED BY Target THEN
    INSERT (TraceId, Description, SomeDateTimeUtc) VALUES (Source.TraceId, Source.Description, GETUTCDATE())
WHEN MATCHED THEN
    UPDATE SET Target.TraceId = Source.TraceId
        -- this is the match condition, nothing will be changed by the UPDATE
        -- I only do this to bring the record into the 'inserted' special table so I can output the ID
OUTPUT inserted.ID INTO @InsertedIDs; 

INSERT INTO [dbo].[ChildTable] (ParentTableId, SomeMessage, StatusDateTimeUtc)
-- Assumption: only one row will be MERGED into ParentTable at a time
-- So, we will always have only one record in the @InsertedIDs
VALUES ((select top 1 ID from @InsertedIDs), 'Some Message', GETUTCDATE())

select * from ParentTable
select * from ChildTable

ROLLBACK
--COMMIT

上述 SQL 仅在一次数据库访问中执行请求的操作。我希望使用实体框架获得相同的结果。

限制:

  1. 我知道
    context.Database.ExecuteSqlCommand(sql, parameters);
    方法。我宁愿避免在代码中硬编码 SQL。
  2. 我熟悉可能对此有所帮助的商业库(例如Entity Framework Extensions)。这些扩展很棒,我可以使用它们。

还有其他解决办法吗?

我在 GitHub 中创建了一个小型 PoC 作为代码的快速启动:https://github.com/mpetcov/MergeAndInsert-poc

sql-server entity-framework merge ef-core-8.0
1个回答
0
投票

我建议另一个扩展linq2db.EntityFrameworkCore,它支持开箱即用的

Merge
。免责声明,我是创作者之一。

使用此扩展,您可以编写以下 LINQ 查询:

var source = new []
{
    new ParentTable {TraceId = new Guid("00000000-9F5D-4D3C-9EDC-65BCBEA2D88F"), Description = "Some Description"},
};

var insertedIds = context.ParentTables
    .ToLinqToDB()
    .Merge()
    .Using(source)
    .On((t, s) => t.TraceId == s.TraceId)
    .InsertWhenNotMatched(s => new ParentTable
    {
        TraceId = s.TraceId,
        Description = s.Description,
        SomeDateTimeUtc = Sql.CurrentTimestampUtc
    })
    .UpdateWhenMatched((t, s) => new ParentTable
    {
        TraceId = t.TraceId,
    })
    .MergeWithOutput((a, deleted, inserted) => inserted.TraceId)
    .ToList();

扩展应生成以下 SQL:

MERGE INTO [ParentTable] [Target]
USING (VALUES
    ('00000000-9f5d-4d3c-9edc-65bcbea2d88f',N'Some Description')
) [Source]
(
    [TraceId],
    [Description]
)
ON ([Target].[TraceId] = [Source].[TraceId])

WHEN NOT MATCHED THEN
INSERT
(
    [TraceId],
    [Description],
    [SomeDateTimeUtc]
)
VALUES
(
    [Source].[TraceId],
    [Source].[Description],
    SYSUTCDATETIME()
)

WHEN MATCHED THEN
UPDATE
SET
    [Target].[TraceId] = [Target].[TraceId]
OUTPUT
    [INSERTED].[TraceId]
;
© www.soinside.com 2019 - 2024. All rights reserved.