当我不知道记录是否存在时,如何使用Entity Framework来执行MERGE?

Jos*_*ank 37 .net entity-framework upsert entity-framework-6

这个关于实体框架和MERGE的SO答案中,如何编写代码的示例如下:

public void SaveOrUpdate(MyEntity entity)
{
  if (entity.Id == 0)
  {
    context.MyEntities.AddObject(entity);
  }
  else
  {
    context.MyEntities.Attach(entity);
    context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
  }
}
Run Code Online (Sandbox Code Playgroud)

这假设您已经知道要嵌入的实体是否存在; 在这种情况下,你检查entity.Id.但是,如果您不知道该项目是否存在,该怎么办?例如,在我的情况下,我将来自供应商的记录导入我的数据库,并且可能已经或可能没有导入给定记录.我想更新记录,如果它存在,否则添加它.但两种情况下都已设置供应商的ID.

除非我只是询问数据库是否已经存在记录,否则我无法看到任何方法这样做,这违背了MERGE的全部目的.

Rob*_*ves 26

我在这种情况下使用AddOrUpdate.但是,我相信它首先会查询数据库,以便决定发布插入或更新.

context.MyEntities.AddOrUpdate(e => e.Id, entity);
Run Code Online (Sandbox Code Playgroud)

更新:

我浏览了调试日志文件.首先它运行:

SELECT TOP (2) ... WHERE 1 = [Extent1].[Id]
Run Code Online (Sandbox Code Playgroud)

然后它运行:

INSERT [dbo].[TestTable](...) VALUES (...)
SELECT [Id]
FROM [dbo].[TestTable]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
Run Code Online (Sandbox Code Playgroud)

要么:

UPDATE [dbo].[TestTable]
SET ...
WHERE ([Id] = @2)
Run Code Online (Sandbox Code Playgroud)

更新2:这是一个有趣的扩展方法,使用MERGE:https: //gist.github.com/ondravondra/4001192

  • 请注意AddOrUpdate方法:http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/ (6认同)
  • 有趣.我想如果要发布两个声明,我宁愿自己管理.我将检查该扩展方法,因为这似乎可以解决问题.(但为什么EntityFramework首先不会那样工作,这似乎是一个明显的性能胜利?) (2认同)

mik*_*dge 11

如果你想要一个没有存储过程的原子数据库UPSERT命令并且你不担心上下文被更新,那么值得一提的是你也可以MERGE在一个ExecuteSqlCommand调用中包装一个嵌入语句:

public void SaveOrUpdate(MyEntity entity)
{
    var sql =  @"MERGE INTO MyEntity
                USING 
                (
                   SELECT   @id as Id
                            @myField AS MyField
                ) AS entity
                ON  MyEntity.Id = entity.Id
                WHEN MATCHED THEN
                    UPDATE 
                    SET     Id = @id
                            MyField = @myField
                WHEN NOT MATCHED THEN
                    INSERT (Id, MyField)
                    VALUES (@Id, @myField);"

    object[] parameters = {
        new SqlParameter("@id", entity.Id),
        new SqlParameter("@myField", entity.myField)
    };
    context.Database.ExecuteSqlCommand(sql, parameters);
}
Run Code Online (Sandbox Code Playgroud)

这并不漂亮,因为它在EF对实体的抽象之外工作,但它允许您利用MERGE命令.


Jon*_*nan 10

AddOrUpdate是一个很好的解决方案,但它不可扩展.需要一个数据库往返来检查实体是否已经存在以及插入或更新实体的一次往返.因此,如果您保存1000个实体,将执行2000数据库往返.

免责声明:我是项目实体框架扩展的所有者

此库允许您在实体框架内执行合并操作,同时显着提高性能.只需要1个数据库往返就可以保存1000个实体.

// Easy to use
context.BulkMerge(customers)

// Easy to customize
context.BulkMerge(customers, operation => {
   operation.ColumnPrimaryKeyExpression = 
        customer => customer.Code;
});
Run Code Online (Sandbox Code Playgroud)

  • 这将是一个很好的解决方案.但它不是免费的.它不能用于生产环境,因为免费下载版本需要每月更新.... (9认同)
  • @user007 - 如果你想要同样快速和免费,那么你将不得不编写类似存储过程的东西,它采用 TableValuedParameter 并包含正确处理和锁定的 MERGE 语句,然后有效地更改你的 DAL 以使用它而不是 EF 进行保存变化。如果您不想被这项工作所困扰并且需要/想要将 EF 保留为 DAL,那么这个相当便宜的库是一个**伟大的**解决方案,因为它可以节省您的时间。 (2认同)