Entity Framework 6:插入或更新

3
我想使用Entity Framework在表中插入或更新一行,具体取决于主键(字符串)是否已存在于表中。
我正在使用以下代码来实现这个目标,该代码是从这里获取的:插入或更新模式
private void InsertOrUpdate(ServiceMonitoring sm)
{
    try
    {
        using (var context = new MyDBEntities())
        {
            context.Entry(sm).State = sm.serviceName == null ?
                EntityState.Added :
                EntityState.Modified;
            context.SaveChanges();
        }
        log.Info("ServiceMonitoring updated");
    }
    catch (Exception ex)
    {
        log.Error("Error updating ServiceMonitoring");
        log.Debug(ex.Message);
    }

}

当行(例如serviceName =“MyService”)已经存在时,它可以正常工作。然后执行UPDATE。但是如果该行不存在,则INSERT失败,并出现以下错误消息:
存储更新、插入或删除语句影响了意外数量的行(0)。自从加载实体以来,可能已修改或删除实体。
非常感谢您的帮助!

我认为这并不能解释我的问题。我在我们很少使用的测试系统中运行此程序。而且,我已将"serviceName"更改为一些奇怪的随机字符序列,这在该表中永远不会出现。 - Malawirel
1个回答

4
在MSDN示例中,更新或插入模式之所以与主键配合使用是因为它被标记为“Identity”字段。当您向DbContext添加实体时,EF会给Id字段一个默认值0,当执行INSERT时,数据库会为该行分配一个唯一值。
您尝试不正确地将“更新或插入”模式调整为使用属性“serviceName”,解决方案是使用您引用的MSDN文章提供的模式,或执行查询。
为了完整起见,为什么您的适配器不起作用? 因为您传入了一个ServiceMonitoring对象,并希望context.Entry(sm)查询正确的数据行。但实际上并没有这样的操作。因此,在您的情况下,EntityState.Added基本上是无法到达的代码。
private void InsertOrUpdate(ServiceMonitoring sm)  
{
    try
    {
        //this brand new DbContext instance has not queried your database, and is not tracking any objects!
        using (var context = new MyDBEntities())  
        {
            //if (sm.serviceName is null) <- should never be null, as it's the ServiceMonitoring object you are passing into the function.
            //The DbContext.Entry() does not know whether or not this actually exists in the database, it only allows you to inform
            //EF about the state of the object.
            context.Entry(sm).State = sm.serviceName == null ?  //<- Always false (unless you *really* want a serviceName to not have a name)
                EntityState.Added : // <- code unreachable
                EntityState.Modified; //This is where the exception occurs, your code is always generating an UPDATE statement.
                                        //When the entry exists in the database, EF's generated UPDATE statement succeeds.
                                        //When the entry does not exist, the UPDATE statement fails.
            context.SaveChanges();
        }
        log.Info("ServiceMonitoring updated");
    }
    catch (Exception ex)
    {
        log.Error("Error updating ServiceMonitoring");
        log.Debug(ex.Message);
    }
}

谢谢。我想我理解了你解释的内容,并会尝试相应地优化我的代码。唯一我不同意的是以下内容:“EF知道在插入语句中不填充主键,因为它将由数据库自动生成。”主键不是由数据库自动生成的,因为它不是一个IDENTITY列。主键只是serviceName列,我将为每个我想在该表中监视的服务填写一个唯一的字符串。 - Malawirel
感谢您的反馈!您是正确的,我已经调整了我的答案。 - Adam Vincent
三年后终于接受了答案,哈哈。迟到总比不来得好,对吧? - Adam Vincent
是的,我今天回顾了所有的问题,发现我没有接受这个相当不错的答案。再次感谢! - Malawirel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接