无法获取要更新实体框架中导航属性的关系
Posted
技术标签:
【中文标题】无法获取要更新实体框架中导航属性的关系【英文标题】:Cannot get relationship to update for navigation properties in entity framework 【发布时间】:2012-03-07 18:11:24 【问题描述】:我目前正在使用 EF4.3 和 Code First。我的对象的创建工作(通过我的视图 - 仅使用自动生成的创建),但是当我尝试编辑一个对象时,它不会保存任何最终与我的导航属性相关的更改。我一直是reading on relationships,但我不明白如何告诉我的上下文关系已经改变。
这是我的实现的一些示例代码。
@* Snippet from my view where I link into my ViewModel. *@
<div class="row">
<div class="editor-label">
@html.LabelFor(model => model.ManagerID)
</div>
<div class="editor-field">
@Html.DropDownListFor(model => model.ManagerID, ViewBag.Manager as SelectList, String.Empty)
@Html.ValidationMessageFor(model => model.ManagerID)
</div>
</div>
这是我的控制器实现(我的编辑的 POST):
[HttpPost]
public ActionResult Edit(ProjectViewModel projectViewModel)
if (ModelState.IsValid)
Project project = new Project();
project.ProjectID = projectViewModel.ProjectID;
project.Name = projectViewModel.Name;
project.ProjectManager = repository.GetUser(projectViewModel.ManagerID);
repository.InsertOrUpdateProject(project);
repository.Save();
return RedirectToAction("Index");
ViewBag.Manager = new SelectList(repository.GetUsers(), "UserID", "FullName", projectViewModel.ManagerID);
return View(projectViewModel);
在我的项目对象中:
public class Project
public int ProjectID get; set;
[Required]
public string Name get; set;
// Navigation Properties
public virtual User Manager get; set;
这是来自存储库(我的上下文所在的位置)的相应方法:
public void InsertOrUpdateProject(Project project)
if (program.ProjectID == default(int))
context.Projects.Add(project);
else
context.Entry(project).State = EntityState.Modified;
需要明确的是,这确实可以更新我的属性,但不会更新我的导航属性(在本例中为 Manager)。感谢任何帮助。
【问题讨论】:
【参考方案1】:将状态设置为Modified
仅将标量属性标记为已修改,而不是导航属性。您有多种选择:
一个 hack(你不会喜欢它)
//...
else
var manager = project.Manager;
project.Manager = null;
context.Entry(project).State = EntityState.Modified;
// the line before did attach the object to the context
// with project.Manager == null
project.Manager = manager;
// this "fakes" a change of the relationship, EF will detect this
// and update the relatonship
从数据库中重新加载项目包括(急切加载)当前经理。然后设置属性。变更跟踪会再次检测到manager的变更并写入UPDATE。
在模型中为 Manager
导航属性公开一个外键属性:
public class Project
public int ProjectID get; set;
[Required]
public string Name get; set;
public int ManagerID get; set;
public virtual User Manager get; set;
现在ManagerID
是一个标量属性,将状态设置为Modified
将包含此属性。此外,您不需要从数据库中加载 Manager 用户,您只需分配从视图中获取的 ID:
Project project = new Project();
project.ProjectID = projectViewModel.ProjectID;
project.Name = projectViewModel.Name;
project.ManagerID = projectViewModel.ManagerID;
repository.InsertOrUpdateProject(project);
repository.Save();
【讨论】:
优秀。谢谢你。我最初已经完成了外键路由,但遇到了一些问题。事实证明,它有效,但我必须使用导航属性定义 [ForeignKey("Name")] 属性,以便正确映射所有内容。接受你的回答。谢谢! @user1240560:使用我的答案中的名称,它实际上应该在没有 ForeignKey 属性的情况下工作(按名称约定进行 FK 检测)。但是,如果您的名称略有不同并且不遵循约定规则,那么是的,您需要注释。 @Slauma 我爱你,其他方法应该适用于 Code First 模型,但是使用 DataBase First 唯一对我有用的是你的 hack,我已经搞砸了好几天,现在我不得不说我非常爱你 未来的感谢【参考方案2】:这里有几个选项,我将列出其中的 3 个:
选项 1:使用 GraphDiff
*这需要将上下文的Configuration.AutoDetectChangesEnabled
设置为true。
只需使用 NuGet 安装 GraphDiff
Install-Package RefactorThis.GraphDiff
然后
using (var context = new Context())
var customer = new Customer()
Id = 12503,
Name = "Jhon Doe",
City = new City() Id = 8, Name = "abc"
;
context.UpdateGraph(customer, map => map.AssociatedEntity(p => p.City));
context.Configuration.AutoDetectChangesEnabled = true;
context.SaveChanges();
有关 GraphDiff 的更多详细信息,请查看here。
选项 2:查找和编辑
使用 EF 搜索您的实体以将其跟踪到上下文中。然后编辑属性。
*这需要将上下文的Configuration.AutoDetectChangesEnabled
设置为true。
var customer = new Customer()
Id = 12503,
Name = "Jhon Doe",
City = new City() Id = 8, Name = "abc"
;
using (var context = new Contexto())
var customerFromDatabase = context.Customers
.Include(x => x.City)
.FirstOrDefault(x => x.Id == customer.Id);
var cityFromDataBase = context.Cities.FirstOrDefault(x => x.Id == customer.City.Id);
customerFromDatabase.Name = customer.Name;
customerFromDatabase.City = cityFromDataBase;
context.Configuration.AutoDetectChangesEnabled = true;
context.SaveChanges();
选项 3:使用标量属性
就性能而言,这是最好的方法,但它会使您的课程因数据库问题而变得混乱。因为您需要创建一个标量(primitive 类型)属性来映射 Id。
*这样就不需要将Configuration.AutoDetectChangesEnabled
设置为true。而且您不需要对数据库进行查询来检索实体(就像前两个选项一样 - 是的 GraphDiff 在幕后完成它!)。
var customer = new Customer()
Id = 12503,
Name = "Jhon Doe",
City_Id = 8,
City = null
;
using (var contexto = new Contexto())
contexto.Entry(customer).State = EntityState.Modified;
contexto.SaveChanges();
【讨论】:
非常感谢,我相信如果Configuration.AutoDetectChangesEnabled = true;
不起作用,请检查其他选项【参考方案3】:
我不确定您所说的导航属性到底是什么意思?你的意思是像外键关系吗?如果是这样,请尝试以下数据注释:
public class Project
public int ProjectID get; set;
[Required]
public string Name get; set;
[ForeignKey("YourNavigationProperty")]
public virtual UserManager get; set;
更新您的 EF 上下文,看看会发生什么?
更新
public class Project
public int ProjectID get; set;
[Required]
public string Name get; set;
[ForeignKey("ManagerId")]
public ManagerModel UserManager get; set;
public class ManagerModel
[Key]
public int ManagerId get; set;
public String ManagerName get; set;
看看有没有用?
【讨论】:
所以我没有明确定义我的 FK 名称。我希望能够将我的 Manager 值设置为一个新的 Manager(这似乎可以工作),但是当我调用 SaveChanges() 时,这些更改实际上并没有持久化到数据库中。我不知道如何做到这一点。以上是关于无法获取要更新实体框架中导航属性的关系的主要内容,如果未能解决你的问题,请参考以下文章