我有以下表格:
[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 仅在一次数据库访问中执行请求的操作。我希望使用实体框架获得相同的结果。
限制:
context.Database.ExecuteSqlCommand(sql, parameters);
方法。我宁愿避免在代码中硬编码 SQL。还有其他解决办法吗?
我在 GitHub 中创建了一个小型 PoC 作为代码的快速启动:https://github.com/mpetcov/MergeAndInsert-poc。
我建议另一个扩展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]
;