C# 无法“添加或更新”实体,因为无法更改Primery密钥

C# 无法“添加或更新”实体,因为无法更改Primery密钥,c#,entity-framework,entity-framework-6,C#,Entity Framework,Entity Framework 6,我正在实现一个导入例程,其中用户将一个特定的格式化字符串粘贴到一个输入字段中,然后将该字段转换为一个实体,然后放入数据库 该算法检查实体是否已经存在,并尝试更新它或将其插入数据库。插入工作正常-更新失败 //considered existing if Name and owning user match. if (db.Captains.Any(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId)) {

我正在实现一个导入例程,其中用户将一个特定的格式化字符串粘贴到一个输入字段中,然后将该字段转换为一个实体,然后放入数据库

该算法检查实体是否已经存在,并尝试更新它或将其插入数据库。插入工作正常-更新失败

//considered existing if Name and owning user match.
if (db.Captains.Any(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId))
{
    var captainToUpdate = db.Captains.Where(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId).SingleOrDefault();

    db.Entry(captainToUpdate).CurrentValues.SetValues(captain);
    db.Entry(captainToUpdate).State = EntityState.Modified;
    await db.SaveChangesAsync();
 }
目前的问题是,这样写的话,它也会尝试更新主键,captainToUpdate Id为0,而captainToUpdate Id已经设置,这会导致异常。属性“Id”是对象密钥信息的一部分,无法修改


我需要更改什么,以便正确更新条目。如果可以避免的话,我不想手动更新每个属性,因为表Captain包含30个列。

尝试类似的方法吗

var captainToUpdate = db.Captains.FirstOrDefault(cpt => cpt.Name == captain.Name &&     cpt.User.Id == UserId);
if(captainToUpdate != null){//Update captain Here
    captainToUpdate.Update(captain);        
}else{//Create captain here
    db.Captains.Add(captain);
}
db.Savechanges();

试试这样的

var captainToUpdate = db.Captains.FirstOrDefault(cpt => cpt.Name == captain.Name &&     cpt.User.Id == UserId);
if(captainToUpdate != null){//Update captain Here
    captainToUpdate.Update(captain);        
}else{//Create captain here
    db.Captains.Add(captain);
}
db.Savechanges();

我不会使用实体Captain将数据传输到UI,而是使用一个DTO对象,该对象具有您想要复制的所有属性,仅此而已。可以从任何对象复制值。所有匹配的属性都将被复制,captainToUpdate中的所有其他属性将不受影响。

我不会使用实体Captain将数据传输到UI,而是使用一个DTO对象,该对象包含所有您要复制的属性,不再有。可以从任何对象复制值。将复制所有匹配的属性,captainToUpdate中的所有其他属性将不受影响。

您可以做的是首先将captainToUpdate的Id设置为与captainToUpdate的Id相同:

captain.Id = captainToUpdate.Id;
db.Entry(captainToUpdate).CurrentValues.SetValues(captain);
await db.SaveChangesAsync();

您可以做的是首先将captain的Id设置为与captainToUpdate的Id相同:

captain.Id = captainToUpdate.Id;
db.Entry(captainToUpdate).CurrentValues.SetValues(captain);
await db.SaveChangesAsync();

我也遇到了同样的问题,并通过扩展方法和反射解决了这个问题,当然最好创建一个独立的类,其中包含一些用于重新影响的cachning,但性能在我的任务中并不重要

public static class EnitityFrameworkHelper
    {
        public static void SetValuesByReflection(this DbPropertyValues propertyValues, object o, IEnumerable<string> properties = null)
        {
            var reflProperties = o.GetType().GetProperties();
            var prop = properties ?? propertyValues.PropertyNames;
            foreach (var p in prop)
            {
                var refp = reflProperties.First(x => x.Name == p);
                var v= refp.GetValue(o);
                propertyValues[p] = v;
            }
        }
    }

另外,在要更新的对象中使用外键时也要小心,可能您也想排除它们。

我也遇到了同样的问题,并通过扩展方法和反射解决了它,当然最好创建带有一些cachning的独立类进行重新完善,但性能在我的任务中并不重要

public static class EnitityFrameworkHelper
    {
        public static void SetValuesByReflection(this DbPropertyValues propertyValues, object o, IEnumerable<string> properties = null)
        {
            var reflProperties = o.GetType().GetProperties();
            var prop = properties ?? propertyValues.PropertyNames;
            foreach (var p in prop)
            {
                var refp = reflProperties.First(x => x.Name == p);
                var v= refp.GetValue(o);
                propertyValues[p] = v;
            }
        }
    }


还要注意要更新的对象中的外键,可能也要排除它们。

为什么不在设置值之前记下键值呢。然后重新设置键。为什么不在设置值之前记录键值呢。当然,这是一个有效的解决方案,但正如我在最后一句话中所说的,我正在寻找一种可以避免这种情况的方法。我不想手动更新30多个属性。只需添加代码即可升级这些属性一次。我最近不得不为10个实体编写类似的代码,每个实体有10-80个属性,具有转换选项集和布尔值等特殊行为。有时候你不得不做一些你不想做的事情。这当然是一个有效的解决方案,但正如我在最后一句中所说的,我正在寻找一种方法来避免这种情况。我不想手动更新30多个属性。只需添加代码即可升级这些属性一次。我最近不得不为10个实体编写类似的代码,每个实体有10-80个属性,具有转换选项集和布尔值等特殊行为。有时候你不得不做一些你不想做的事情。如果有任何用处的话。我正在将数据从UI传输到服务器。这是一个字符串,它被解析并压缩为一个实体类型captain,因为这是最适合的。害羞的ID和导航属性。其实没关系,你可以一直使用DTO类型。不管怎样,您都必须知道应该复制哪些属性。AutoMapper配置被忽略的属性也可以完成这项工作。DTO类型的问题是,您需要两个几乎相同的属性,一个用于更新-ID,另一个用于所有其他您仍然需要在UI中标识实体以进行删除的操作。我不会说这是个问题。这是设计选择的结果。如果它有任何用途的话。我正在将数据从UI传输到服务器。这是一个字符串,它被解析并压缩为一个实体类型captain,因为这是最适合的。害羞的ID和导航属性。其实没关系,你可以一直使用DTO类型。不管怎样,您都必须知道应该复制哪些属性。AutoMapper配置被忽略的属性也可以完成这项工作。DTO类型的问题是,您需要两个几乎相同的属性,一个用于更新-ID,另一个用于所有其他您仍然需要在UI中标识实体以进行删除的操作。我不会说这是个问题。这是设计选择的结果。这给了我找到问题的线索:谢谢。非常有用。总之:在调用CurrentValues.SetValues语句之前,首先将UI中对象的Id指定为与db中对象的Id相同。这给了我找到问题的线索:谢谢。非常有用。总之,B 在调用CurrentValues.SetValues语句之前,首先将UI中的对象Id指定为与db中的对象相同。