首先,我想说我已阅读相关帖子(特别是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)。
然而,我无法解决我的问题。我对Entity Framework很新,所以我想我一定是误解了这些帖子的答案。 无论如何,如果有人能帮我理解,我会非常感激,因为我很困惑。
我有两张桌子:
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
属性在数据库中正确更新。
但是,如果我在person.Items
之后检查SaveChanges()
,旧项目仍然会显示而不是新项目,我需要它才能更正页面的控件值。
虽然我看了同样问题的帖子但我无法解决它...
我尝试了很多东西,特别是调用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(更新数据库和属性)
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()
}
}
}
答案 0 :(得分:4)
如何刷新上下文以确保在public T Example2(object obj1, object obj2)
方法之后获得最新的数据库更改。在上下文中传递要刷新的实体.SaveChanges()
:
Refresh
或保留((IObjectContextAdapter)_dbContext).ObjectContext.Refresh(RefreshMode.StoreWins, entityPassed);
方法,并采用更动态的方法:
Commit()
var changedEntities = (from item in context.ObjectStateManager.GetObjectStateEntries(
EntityState.Added
| EntityState.Deleted
| EntityState.Modified
| EntityState.Unchanged)
where item.EntityKey != null
select item.Entity);
context.Refresh(RefreshMode.StoreWins, changedEntities);
只表示数据库(存储)具有优先级,并将覆盖客户端(内存中)更改。
如果Refresh方法不起作用,您可以考虑以下事项:
RefreshMode.StoreWins
或者,如果所有其他方法都失败了,那么在完成每个事务后(在这种情况下,在public void RefreshEntity(T entity)
{
_dbContext.Entry<T>(entity).Reload();
}
被调用之后),请保持简单并使用Dispose
DbContext。然后,如果您需要在提交后使用结果,请将其视为新事务,并实例化新的DbContext并再次加载必要的数据。
答案 1 :(得分:0)
以Transection为例。 它工作正常。
public class UnitOfWork : IUnitOfWork
{
public readonly DatabaseContext _context;
private readonly IDbTransaction _transaction;
private readonly ObjectContext _objectContext;
public UnitOfWork(DatabaseContext context)
{
_context = context as DatabaseContext ?? new DatabaseContext ();
this._objectContext = ((IObjectContextAdapter)this._context).ObjectContext;
if (this._objectContext.Connection.State != ConnectionState.Open)
{
this._objectContext.Connection.Open();
this._transaction = _objectContext.Connection.BeginTransaction();
}
}
public int Complete()
{
int result = 0;
try
{
result = _context.SaveChanges();
this._transaction.Commit();
}
catch (Exception ex)
{
Rollback();
}
return result;
}
private void Rollback()
{
this._transaction.Rollback();
foreach (var entry in this._context.ChangeTracker.Entries())
{
switch (entry.State)
{
case System.Data.Entity.EntityState.Modified:
entry.State = System.Data.Entity.EntityState.Unchanged;
break;
case System.Data.Entity.EntityState.Added:
entry.State = System.Data.Entity.EntityState.Detached;
break;
case System.Data.Entity.EntityState.Deleted:
entry.State = System.Data.Entity.EntityState.Unchanged;
break;
}
}
}
public void Dispose()
{
if (this._objectContext.Connection.State == ConnectionState.Open)
{
this._objectContext.Connection.Close();
}
_context.Dispose();
}
}