C# 保存包含列表的对象时,会给出;引用完整性约束冲突“;
我试图理解如何使用MVC和实体框架编辑一对多关系,我遇到了一些问题,我试图编辑同一视图中的一个(C# 保存包含列表的对象时,会给出;引用完整性约束冲突“;,c#,asp.net-mvc,entity-framework,one-to-many,C#,Asp.net Mvc,Entity Framework,One To Many,我试图理解如何使用MVC和实体框架编辑一对多关系,我遇到了一些问题,我试图编辑同一视图中的一个(Person)和多个(Color)(一旦完成,我将转到多对多) 我看过很多其他的帖子,没有看到一个直接解决我所经历的问题的方法 BaseObject类: public class BaseObject { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Key] [Column(Order = 1)] pub
Person
)和多个(Color
)(一旦完成,我将转到多对多)
我看过很多其他的帖子,没有看到一个直接解决我所经历的问题的方法
BaseObject
类:
public class BaseObject
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Column(Order = 1)]
public Guid Oid { get; set; }
}
public class Person : BaseObject
{
public string Name { get; set; }
public virtual List<Color> Colors { get; set; }
}
public class Color : BaseObject
{
public string Name { get; set; }
[DisplayName("Person")]
public Guid? PersonID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Person { get; set; }
}
Person
Class:
public class BaseObject
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Column(Order = 1)]
public Guid Oid { get; set; }
}
public class Person : BaseObject
{
public string Name { get; set; }
public virtual List<Color> Colors { get; set; }
}
public class Color : BaseObject
{
public string Name { get; set; }
[DisplayName("Person")]
public Guid? PersonID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Person { get; set; }
}
在此之后,我创建了我的Scafolding(和视图),在此基础上,我修改了Person/Edit.cshtml
,以@for(var I=0;I模型绑定器
方法:
当我运行应用程序时,打开一个现有的人,我会看到这个人的名字和颜色列表(蓝色和绿色)。修改与否,当我点击“保存”时,db.Entry(person.State=EntityState.Modified代码>返回以下错误:
System.InvalidOperationException:'发生引用完整性约束冲突:关系一端的'Person.Oid'属性值与另一端的'Color.PersonID'属性值不匹配。'
在Person
对象处检查,它确实有2种颜色(使用模型绑定器获得),进入Colors
集合我看到颜色的名称已更改,Person
对象已填充,但PersonID
未填充
person.Colors[0].Name "Blue updated" string
person.Colors[0].PersonID null System.Guid?
+person.Colors[0].Person {x.Models.One_to_Many.Person} x.Models.One_to_Many.Person
我相信PersonID
是我的问题,它需要用当前的Person
填充
我是否需要绑定或包含在编辑获取中
public ActionResult Edit(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Person person = db.One.Find(id);
if (person == null)
{
return HttpNotFound();
}
return View(person);
}
我不确定最好的方法是使用此设置或将其设置为“保存”
编辑
这是基于@gert arnold关于使用ViewModel的建议,我的计划是在我确定一对多和未来的多对多之后再使用ViewModel
保存模型的效果不是很好,如果你看到下面,我将能够使代码适应PeopleController,但将其放入ViewModel和Controller中肯定是有意义的
我现在创建了一个PersonViewModel
:
public class PersonView
{
public string Name { get; set; }
public virtual List<Color> Colors { get; set; }
}
最后,Edit.cshtml
与上面的非常相似(如果不是相同的话)。我相信我正在得到我想要的结果,我可以扩展以允许新的Color
s与Ajax内联添加,等等
我知道我需要检查并捕捉错误,但我觉得这足以作为概念的证明。简言之,是否有人对该代码和方法有任何建议,我现在希望对其进行改进,并将其应用于我的实际项目。考虑删除颜色实体中的PersonId列。设置映射以将“Person”键解析为PersonId列,但避免在子实体中同时公开父引用及其FK属性。这会导致各种问题,其中父引用和FK可能会不同步。根据映射的解析方式,这两个属性都已就位,您需要对所有引用属性执行如下操作:
private Person _person = null;
public virtual Person Person
{
get {return _person;}
set
{
_person = value;
if (value != null)
PersonID = value.OId;
else
PersonID = null;
}
}
免责声明:我个人不使用代码优先映射。我选择先在EntityTypeConfiguration声明中使用DB,因为我使用的是有界上下文,并且更喜欢在没有ORM“修补”的情况下优化模式。通过显式配置,我可以使用.Map解析FK,而无需将其作为实体中的属性公开。我不知道首先使用代码是否/如何实现这一点
作为一般规则,我避免完全添加父实体引用,除非我知道需要反向解析。例如:如果我总是从Person转到list Color,我的Color对象中甚至不会有Person引用(或FK属性)。然而,对于像客户和订单这样的情况,我认为在我的订单上有一个客户参考是合理的,因为我希望搜索订单,并希望看到它们与什么客户关联 开始使用视图模型是一个明智的选择。这样做时,有多种方法可以“绘制状态”返回到EF实体。看看人
,其中一个是:
person = db.People.Find(id);
person.Name = personViewModel.Name;
db.SaveChanges();
通过Find
语句,EF开始跟踪person
实体。EF注意到名称的修改,并将进行更新。将状态设置为Modified
是不必要的,甚至不是最理想的,因为它会导致EF更新所有person
的属性
绘制状态的另一种方法是:
person = new Person { Oid = personViewModel.Oid, Name = personViewModel.Name };
db.Persons.Attach(person);
db.Entry(person).Property(p => p.Name).IsModified = true;
db.SaveChanges();
这将删除数据库往返,这可能会提高性能,但需要更多的代码,特别是当涉及嵌套对象时。另一个主要缺点是,person
,即所谓的存根实体,现在很可能无法通过验证,因为所需的属性可能是null
。这可以通过禁用save时的验证来避免(db.Configuration.ValidateOnSaveEnabled=false
),但是您需要EF提供的验证以外的其他机制
有关如何在这些选项之间进行选择的详细信息。只要不需要指定嵌套对象绑定,Bind
属性就可以了。除非绝对必要,否则我不会用它。使用视图模型/DTO控制对模型属性的访问。另一件事是,db.Entry(person).State=EntityState.Modified
不会将嵌套对象标记为Modified
,只标记为基本属性。因此,我的建议是:在MVC的VC部分使用PersonDto/ColorDto,而不使用[Bind]
,并将它们映射回控制器或(最好)服务中的真实内容。谢谢@gert arnold,我已经用ViewModel更新了我的初始问题,效果很好。