我正在实现一个导入例程,用户将特定的格式化字符串粘贴到输入字段中,然后输入字段转换为实体,然后放入数据库。
算法检查实体是否已存在,并尝试更新它或将其插入数据库。插入工作正常 - 更新失败。
//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
Id已设置),这会导致异常The property 'Id' is part of the object's key information and cannot be modified.
我需要更改什么,以便正确更新enttiy。如果可以避免,我不想手动更新每个属性,因为表Captain包含30列。
答案 0 :(得分:2)
我不会使用实体Captain
将数据传输到UI,而是使用包含要复制的所有属性的DTO对象。 您可以复制任何对象的值。将复制所有匹配的属性,captainToUpdate
中的所有其他属性都不会受到影响。
答案 1 :(得分:2)
您可以做的是首先将队长的Id设置为与captainToUpdate的Id相同。在这种情况下使用:
db.Entry(captainToUpdate).CurrentValues.SetValues(captain);
会奏效。
答案 2 :(得分: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();
答案 3 :(得分: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"}));
另外要注意要更新的对象中的外键,可能还要排除它们。