我得到这个错误时,我GetById()在一个实体,然后设置子实体的集合到我的新列表,来自MVC视图。
操作失败
关系是无法改变的
因为一个或多个外键
Properties是非空的。当一个
关系发生了变化
相关外键属性设置为
空值。如果外键是
不支持空值,新建
关系必须被定义
必须分配外键属性
另一个非空值或
必须删除不相关的对象。
我不太理解这句话:
这种关系无法改变
因为一个或多个外键
Properties是非空的。
我为什么要改变两个实体之间的关系?它应该在整个应用程序的生命周期内保持不变。
发生异常的代码只是简单地将集合中修改过的子类分配给现有的父类。这将有望满足取消子类,增加新的和修改。我本以为实体框架处理这个。
代码行可以提炼为:
var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();
我也解决了我的问题与Mosh的答案,我认为PeterB的答案是有点,因为它使用枚举作为外键。请记住,在添加这段代码之后,您将需要添加一个新的迁移。
我也可以推荐这篇博客的其他解决方案:
http://www.kianryan.co.uk/2013/03/orphaned-child/
代码:
public class Child
{
[Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Heading { get; set; }
//Add other properties here.
[Key, Column(Order = 1)]
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
如果你正在使用自动绘图器,面临以下问题是一个很好的解决方案,它为我工作
https://www.codeproject.com/Articles/576393/Solutionplusto-aplus-Theplusoperationplusfailed
因为问题是我们正在映射空导航属性,我们实际上不需要在实体上更新它们,因为它们在契约上没有改变,我们需要在映射定义上忽略它们:
ForMember(dest => dest.RefundType, opt => opt.Ignore())
所以我的代码是这样的:
Mapper.CreateMap<MyDataContract, MyEntity>
ForMember(dest => dest.NavigationProperty1, opt => opt.Ignore())
ForMember(dest => dest.NavigationProperty2, opt => opt.Ignore())
.IgnoreAllNonExisting();
我今天遇到了这个问题,想分享我的解决方案。在我的例子中,解决方案是在从数据库中获取Parent之前删除Child项。
以前我是这样做的代码如下。然后我将得到这个问题中列出的相同错误。
var Parent = GetParent(parentId);
var children = Parent.Children;
foreach (var c in children )
{
Context.Children.Remove(c);
}
Context.SaveChanges();
对我来说,有效的方法是先获取子项,使用parentId(外键),然后删除这些项。然后我可以从数据库中获取父元素,在这一点上,它应该不再有任何子元素,我可以添加新的子元素。
var children = GetChildren(parentId);
foreach (var c in children )
{
Context.Children.Remove(c);
}
Context.SaveChanges();
var Parent = GetParent(parentId);
Parent.Children = //assign new entities/items here
我只是犯了同样的错误。
我有两个具有父子关系的表,但是我在子表的表定义中的外键列上配置了“on delete cascade”。
因此,当我手动删除父行(通过SQL)在数据库中,它将自动删除子行。
然而,这在EF中不起作用,出现了这个线程中描述的错误。
原因是,在我的实体数据模型(edmx文件)中,父表和子表之间的关联属性不正确。
End1的OnDelete选项被配置为none(“End1”在我的模型中是具有1的多重性的结束)。
我手动将End1 OnDelete选项改为Cascade,然后它就工作了。
我不知道为什么EF不能拾取这个,当我从数据库更新模型(我有一个数据库第一模型)。
为了完整起见,这是我删除代码的样子:
public void Delete(int id)
{
MyType myObject = _context.MyTypes.Find(id);
_context.MyTypes.Remove(myObject);
_context.SaveChanges();
}
如果我没有定义级联删除,我将不得不在删除父行之前手动删除子行。
出现这个问题是因为我们试图删除父表,但仍然存在子表数据。
我们利用级联删除来解决这个问题。
在模型中创建dbcontext类中的方法。
modelBuilder.Entity<Job>()
.HasMany<JobSportsMapping>(C => C.JobSportsMappings)
.WithRequired(C => C.Job)
.HasForeignKey(C => C.JobId).WillCascadeOnDelete(true);
modelBuilder.Entity<Sport>()
.HasMany<JobSportsMapping>(C => C.JobSportsMappings)
.WithRequired(C => C.Sport)
.HasForeignKey(C => C.SportId).WillCascadeOnDelete(true);
之后,在我们的API调用中
var JobList = Context.Job
.Include(x => x.JobSportsMappings) .ToList();
Context.Job.RemoveRange(JobList);
Context.SaveChanges();
级联删除选项删除父表以及与父相关的子表,使用这个简单的代码。用这个简单的方法试试。
删除范围用于删除数据库中的记录列表
谢谢
这是一个很大的问题。在你的代码中实际发生的是这样的:
从数据库加载Parent并获得附加实体
您用分离的子集合替换它的子集合
您保存更改,但在此操作期间,所有的子元素都被认为是添加的,因为EF直到此时才知道它们。因此EF尝试将null设置为旧子节点的外键,并插入所有新的子节点=>重复行。
解决方法取决于你想做什么以及你想怎么做?
如果您正在使用ASP。NET MVC你可以尝试使用UpdateModel或TryUpdateModel。
如果你只是想手动更新现有的子程序,你可以简单地这样做:
foreach (var child in modifiedParent.ChildItems)
{
context.Childs.Attach(child);
context.Entry(child).State = EntityState.Modified;
}
context.SaveChanges();
附加实际上是不需要的(将状态设置为Modified也将附加实体),但我喜欢它,因为它使过程更加明显。
如果你想修改现有的,删除现有的和插入新的子元素,你必须这样做:
var parent = context.Parents.GetById(1); // Make sure that childs are loaded as well
foreach(var child in modifiedParent.ChildItems)
{
var attachedChild = FindChild(parent, child.Id);
if (attachedChild != null)
{
// Existing child - apply new values
context.Entry(attachedChild).CurrentValues.SetValues(child);
}
else
{
// New child
// Don't insert original object. It will attach whole detached graph
parent.ChildItems.Add(child.Clone());
}
}
// Now you must delete all entities present in parent.ChildItems but missing
// in modifiedParent.ChildItems
// ToList should make copy of the collection because we can't modify collection
// iterated by foreach
foreach(var child in parent.ChildItems.ToList())
{
var detachedChild = FindChild(modifiedParent, child.Id);
if (detachedChild == null)
{
parent.ChildItems.Remove(child);
context.Childs.Remove(child);
}
}
context.SaveChanges();