【问题标题】:Property not updated after SaveChanges (EF database first)SaveChanges 后属性未更新(EF 数据库优先)
【发布时间】:2015-05-29 08:10:25
【问题描述】:

首先,我想说我阅读了相关的帖子(特别是EF 4.1 SaveChanges not updating navigation or reference propertiesEntity 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 带有可空的 PersonIdType

一个项目可以有一个所有者,也可以没有。 因此,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 =&gt; 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 =&gt; i.PersonId == person.Id &amp;&amp; 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


【解决方案1】:

如何刷新上下文以确保在 .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);

RefreshMode.StoreWins 仅表示数据库(存储)具有优先权,并将覆盖客户端(内存中)的更改。

如果刷新方法不起作用,可以考虑以下:

public void RefreshEntity(T entity)
{
  _dbContext.Entry<T>(entity).Reload();
}

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

【讨论】:

  • 非常感谢您的回复!但是我不确定我是否理解得很好:我可以保留Commit() 方法,并创建一个RefreshEntity() 方法来执行_dbContext.Refresh(RefreshMode.StoreWins, entityPassed);。是这样吗?
  • 看来我误读了您的评论。我道歉。是的,RefreshEntity 方法应该很好用!
  • 非常感谢,我马上试试。但是,我的_dbcontext 没有Refresh() 方法,也没有ObjectStateManager。我不知道这是否重要,但MyEntities 类型继承自DbContext
  • 查看编辑后的答案。看看您现在是否可以访问您的 Refresh() 方法?
  • 是的,成功了,谢谢:) 我用我尝试过的方法编辑了我的第一篇文章(我在Commit() 方法中的SaveChanges() 之后添加了刷新),不幸的是这似乎没有工作,数据库是正确的,但person.Items 没有更新。我是不是做错了什么?
【解决方案2】:

以横断面为例。 它工作正常。

 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();
    }
}

【讨论】:

    猜你喜欢
    • 2018-02-26
    • 1970-01-01
    • 1970-01-01
    • 2021-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多