【发布时间】:2015-05-29 08:10:25
【问题描述】:
首先,我想说我阅读了相关的帖子(特别是EF 4.1 SaveChanges not updating navigation or reference properties、Entity Framework Code First - Why can't I update complex properties this way?和Entity Framework 4.1 RC (Code First) - Entity not updating over association)。
但是,我无法解决我的问题。我对实体框架很陌生,所以我想我一定误解了那些帖子的答案。 无论如何,我真的很感激有人可以帮助我理解,因为我很困惑。
我有两张桌子:
-
Person -
Item带有可空的PersonId和Type
一个项目可以有一个所有者,也可以没有。
因此,Person 有一个 Items 属性,它是 Item 的 IEnumerable。
一个人只能按类型拥有一个Item。
如果这个人想改变,他可以将他当前的项目替换为他的项目中任何其他相同类型的项目:
public class MyService
{
private PersonRepo personRepo = new PersonRepo();
private ItemRepo itemRepo = new ItemRepo();
public void SwitchItems(Person person, Guid newItemId)
{
using (var uof = new UnitOfWork())
{
// Get the entities
Item newItem = itemRepo.Get(newItemId);
Item oldItem = person.Items.SingleOrDefault(i => i.Type == newItem.Type)
// Update the values
newItem.PersonId = person.Id;
oldItem.PersonId = null;
// Add or update entities
itemRepo.AddOrUpdate(oldItem);
itemRepo.AddOrUpdate(newItem);
personRepo.AddOrUpdate(person);
uof.Commit(); // only does a SaveChanges()
}
}
}
这是存储库结构和AddOrUpdate 方法:
public class PersonRepo : RepositoryBase<Person>
{
...
}
public class RepositoryBase<TObject> where TObject : class, IEntity
{
protected MyEntities entities
{
get { return UnitOfWork.Current.Context; }
}
public virtual void AddOrUpdate(TObject entity)
{
if (entity != null)
{
var entry = entities.Entry<IEntity>(entity);
if (Exists(entity.Id))
{
if (entry.State == EntityState.Detached)
{
var set = entities.Set<TObject>();
var currentEntry = set.Find(entity.Id);
if (currentEntry != null)
{
var attachedEntry = entities.Entry(currentEntry);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
set.Attach(entity);
entry.State = EntityState.Modified;
}
}
else
entry.State = EntityState.Modified;
}
else
{
entry.State = EntityState.Added;
}
}
}
}
这很好用,新旧项目的PersonId 属性在数据库中正确更新。
但是,如果我在SaveChanges() 之后检查person.Items,则旧项目仍然出现而不是新项目,我需要它正确才能更新页面的控件值。
虽然我阅读了同样问题的帖子,但我无法解决它......
我尝试了很多东西,特别是调用entities.Entry(person).Collection(p => p.Items).Load(),但每次尝试都会出现异常。
如果有人有任何想法,请随意,如果需要,我可以添加更多代码。
非常感谢!
编辑:UnitOfWork
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
public class UnitOfWork : IDisposable
{
private const string _httpContextKey = "_unitOfWork";
private MyEntities _dbContext;
public static UnitOfWork Current
{
get { return (UnitOfWork)HttpContext.Current.Items[_httpContextKey]; }
}
public UnitOfWork()
{
HttpContext.Current.Items[_httpContextKey] = this;
}
public MyEntities Context
{
get
{
if (_dbContext == null)
_dbContext = new MyEntities();
return _dbContext;
}
}
public void Commit()
{
_dbContext.SaveChanges();
}
public void Dispose()
{
if (_dbContext != null)
_dbContext.Dispose();
}
}
两种有效的解决方案
解决方案 1(SaveChanges 后从上下文重新加载)
public partial class MyPage
{
private MyService service;
private Person person;
protected void Page_Load(object sender, EventArgs e)
{
service = new MyService();
person = service.GetCurrentPerson(Request.QueryString["id"]);
...
}
protected void SelectNewItem(object sender, EventArgs e)
{
Guid itemId = Guid.Parse(((Button)sender).Attributes["id"]);
service.SelectNewItem(person, itemId);
UpdatePage();
}
private void UpdatePage()
{
if (person != null)
person = service.GetCurrentPerson(Request.QueryString["id"]);
// Update controls values using person's properties here
}
}
public class MyService
{
private PersonRepo personRepo = new PersonRepo();
private ItemRepo itemRepo = new ItemRepo();
public void SwitchItems(Person person, Guid newItemId)
{
using (var uof = new UnitOfWork())
{
// Get the entities
Item newItem = itemRepo.Get(newItemId);
Item oldItem = person.Items.SingleOrDefault(i => i.Type == newItem.Type)
// Update the values
newItem.PersonId = person.Id;
oldItem.PersonId = null;
// Add or update entities
itemRepo.AddOrUpdate(oldItem);
itemRepo.AddOrUpdate(newItem);
personRepo.AddOrUpdate(person);
uof.Commit(); // only does a SaveChanges()
}
}
}
解决方案 2(更新数据库 AND 属性)
public partial class MyPage
{
private MyService service;
private Person person;
protected void Page_Load(object sender, EventArgs e)
{
service = new MyService();
person = service.GetCurrentPerson(Request.QueryString["id"]);
...
}
protected void SelectNewItem(object sender, EventArgs e)
{
Guid itemId = Guid.Parse(((Button)sender).Attributes["id"]);
service.SelectNewItem(person, itemId);
UpdatePage();
}
private void UpdatePage()
{
// Update controls values using person's properties here
}
}
public class MyService
{
private PersonRepo personRepo = new PersonRepo();
private ItemRepo itemRepo = new ItemRepo();
public void SwitchItems(Person person, Guid newItemId)
{
using (var uof = new UnitOfWork())
{
// Get the entities
Item newItem = itemRepo.Get(newItemId);
Item oldItem = person.Items.SingleOrDefault(i => i.Type == newItem.Type)
// Update the values
newItem.PersonId = person.Id;
oldItem.PersonId = null;
person.Items.Remove(oldItem);
person.Items.Add(newItem);
// Add or update entities
itemRepo.AddOrUpdate(oldItem);
itemRepo.AddOrUpdate(newItem);
personRepo.AddOrUpdate(person);
uof.Commit(); // only does a SaveChanges()
}
}
}
【问题讨论】:
-
我不确定更改实体上的外键会触发 EF 将其放入正确的集合中。如果你启动一个新的上下文并从数据库重新加载会发生什么?
-
非常感谢您的回复。是的,如果我从数据库重新加载它,它可以工作(顺便说一下,它也可以在以下 Page_Load 之后工作)。如果我在 SaveChanges 之后检查
Item currentItem = entities.Items.SingleOrDefault(i => i.PersonId == person.Id && i.TypeId == newItem.TypeId);,currentItem完全没问题。但我真的需要该属性是最新的才能更新页面控件。目前我需要刷新页面(通过Page_Load并重新加载当前人)才能看到item发生了变化。 -
@jlvaquero : 当然,我用 UnitOfWork 代码编辑了我的第一篇文章 :)
-
我建议如果您想从该人的项目集合中删除
Item,那么只需在上下文跟踪它时将其从集合中删除,这将使SaveChanges时的外键无效叫做。我会说这更像是一种 ORM 做事方式,而不是使用外键 -
你是说
person.Items.Remove(oldItem); person.Items.Add(newItem)而不是newItem.PersonId = person.Id; oldItem.PersonId = null;?
标签: c# entity-framework