Прежде всего, я хотел бы сказать, что я прочитал соответствующие должности (в частности, 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).Недвижимость не обновляется после SaveChanges (сначала база данных EF)
Однако я не мог решить свою проблему. Я совершенно новичок в Entity Framework, поэтому, наверное, я неправильно понял ответы на эти сообщения. В любом случае я был бы очень благодарен, если кто-то может помочь мне понять, потому что я совершенно застрял.
У меня есть две таблицы:
Person
Item
с обнуляемогоPersonId
иType
Элемент может иметь владельца, или нет. Следовательно, Person
имеет свойство Items
, которое является IEnumerable of Item
.
Человек может иметь только один 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()
, но получал исключение каждый раз, когда пытался.
Если у кого-то есть идеи, пожалуйста, не стесняйтесь, я могу добавить еще какой-нибудь код, если это необходимо.
Большое спасибо!
EDIT: 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 (Reload из контекста после 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()
}
}
}
Я не уверен, что изменение внешнего ключа на хозяйствующий субъект инициирует EF, чтобы поместить его в правильной коллекции. Что произойдет, если вы создадите новый контекст и перезагрузитесь из db? – user2697817
Большое спасибо за ваш ответ. Да, если я перезагружу его из базы данных, он работает (он также работает после следующей Page_Load, кстати). Если я проверю 'Item currentItem = entities.Items.SingleOrDefault (i => i.PersonId == person.Id && i.TypeId == newItem.TypeId);' сразу после SaveChanges, 'currentItem' отлично справляется. Но мне действительно нужно, чтобы свойство было актуальным, чтобы обновить элементы управления страницей. В настоящее время мне нужно обновить страницу (перейдите по странице_Load и перезагрузите текущего пользователя), чтобы увидеть, что элемент изменился. –
@jlvaquero: Конечно, я отредактировал свой первый пост с кодом UnitOfWork :) –