SaveChanges(EF数据库优先)后属性未更新

Fla*_*ack 11 c# entity-framework

首先,我想说我读了相关帖子(特别是EF 4.1 SaveChanges没有更新导航或参考属性,实体框架代码优先 - 为什么我不能用这种方式更新复杂属性?Entity Framework 4.1 RC(代码优先) - 实体不通过关联更新).

但是,我无法解决我的问题.我对Entity Framework很新,所以我想我一定是误解了这些帖子的答案.无论如何,我真的很感激有人可以帮助我理解,因为我很困惑.

我有两张桌子:

  • Person
  • Item有一个可空PersonId和一个Type

项目可以拥有所有者,也可以不拥有.因此,Person具有ItemsIEnumerable的属性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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是存储库结构和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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这非常有效,旧项目和新项目的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();
    }
}
Run Code Online (Sandbox Code Playgroud)

两个有效的解决方案

解决方案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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Nie*_*ter 5

如何刷新您的上下文以确保您在.SaveChanges()方法之后拥有最新的数据库更改。传入要刷新的实体对Refresh上下文的调用:

((IObjectContextAdapter)_dbContext).ObjectContext.Refresh(RefreshMode.StoreWins, entityPassed);
Run Code Online (Sandbox Code Playgroud)

或者保持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);
Run Code Online (Sandbox Code Playgroud)

RefreshMode.StoreWins简单地表明,数据库(存储)的优先级,并会覆盖客户端(内存)的变化。

如果 Refresh 方法不起作用,您可以考虑以下方法:

public void RefreshEntity(T entity)
{
  _dbContext.Entry<T>(entity).Reload();
}
Run Code Online (Sandbox Code Playgroud)

或者,如果所有其他方法都失败了,请Dispose在完成每个事务后保持简单和DbContext(在本例SaveChanges()中是在调用之后)。然后,如果您需要在提交后使用结果,请将其视为一个新事务,然后实例化一个新的 DbContext 并再次加载您需要的数据。