2014-08-28 21 views
0

我正在实现一个导入例程,其中用户将特定的格式化字符串粘贴到输入字段中,然后将输入字段转换为实体,然后放入数据库。不能“添加或更新”实体,因为Primery密钥不能更改

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

//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(); 
} 

眼前的问题是,这样写的,它会尝试更新主键为好,(captain ID为0,而captainToUpdate标识已被设置),这导致异常The property 'Id' is part of the object's key information and cannot be modified.

我需要更改什么,以便正确更新实体。如果可以避免,我不想手动更新每个属性,因为表队长包含30个列。

+0

为什么不在设置值之前记下键值。然后将钥匙重置。 – 2014-08-28 13:04:11

回答

2

我不会用实体Captain将数据传输到用户界面,但有要复制的所有属性,并没有更多的DTO的对象。 您可以从任何对象复制值。所有匹配的属性将被复制,captainToUpdate中的所有其他属性将不受影响。

+0

如果有任何用处。我将数据从用户界面传输到服务器。它是一个字符串,它被解析并压入一个实体类型队长,因为这是最适合的。身份证和导航属性很害羞。 – Marco 2014-08-29 04:40:34

+1

并不重要,你可以随时使用DTO类型。无论如何,你必须告诉哪些属性应该复制。 AutoMapper(配置忽略的属性)也可以完成这项工作。 – 2014-08-29 06:52:05

1

尝试这样?

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(); 
+0

这当然是一个有效的解决方案,但正如我的最后一句话所述,我正在寻找一种避免这种情况的方法。我不想手动更新30多个物业。 – Marco 2014-08-28 13:44:45

+0

创建方法更新需要队长参数... – 2014-08-28 13:47:38

+1

您只需添加代码即可升级这些属性。我最近不得不为10个具有10-80个属性的实体编写类似的代码,其中包含转换选项集和布尔值等特殊行为。有时你必须做一些你不想做的事情。 – Nzall 2014-08-28 13:48:44

2

你可以做的是首先将队长的ID设置为与队长更新的ID相同。在这种情况下使用:

db.Entry(captainToUpdate).CurrentValues.SetValues(captain); 

将工作。

+0

这给了我找到问题的线索:) – Romias 2017-11-16 00:26:52

0

我遇到了同样的问题,并通过扩展方法和反射来解决它,当然最好是创建一个独立的类,并重新安装一些缓存,但性能对我的任务并不重要。

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; 
      } 
     } 
    } 

,这里是例如如何使用它

var entry = ctx.Entry(accSet); 
entry.CurrentValues.SetValuesByReflection(eParameters, entry.CurrentValues.PropertyNames.Except(new [] { "ID"})); 

而且要小心要更新对象外键,大概要排除他们。

相关问题