【问题标题】:Updating an entity using EF 4.3 code first首先使用 EF 4.3 代码更新实体
【发布时间】:2012-05-02 12:28:47
【问题描述】:

我有以下实体

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Sections Sections { get; set; }
}

public class Section
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class Sections : List<Section>, ICollection<Section>
{ }

我添加了一个项目 ID 为 1 的新项目,但其中没有任何部分...

现在我想修改项目并在其中添加新部分,为此,我在上下文中检索该项目并将其保存在另一个上下文中。在这里,我必须使用两个单独的上下文,因为不确定用户何时要更新对象。在那之前我不能让上下文对象保持打开状态。

        Item fAp;
        using (var ctx = new MyContext())
        {
            fAp = ctx.Items.Include(a => a.Sections).Single(a => a.Id == 1);
        }

        using (var ctx2 = new MyContext())
        {
            var nsec = new Section()
            {
                ID = 1,
                Name = "App Sec...1"
            };

            fAp.Sections.Add(nsec);
            ctx2.Entry(fAp).State = EntityState.Modified;
            ctx2.SaveChanges();
        }

通过这样做,新的部分没有被添加到数据库中。然后我修改了代码并在调用 ctx2.SaveChanges() 之前添加了以下行。

            ctx2.Entry(nsec).State = EntityState.Added;

保存成功。

现在,我的问题是我有非常复杂的数据层次结构,我的意思是 Section 类也有很多子导航属性,这些属性还有很多,等等。

跟踪所有此类属性并手动设置它们的状态将非常困难。这种情况下如何更新数据?

还有一件事,我不会使用急切加载,我的意思是在检索 Item 对象时我不会使用“包含”。我将在需要时使用显式加载,最后想要更新对象。

有什么好的建议吗?

【问题讨论】:

    标签: .net database entity-framework


    【解决方案1】:

    设置状态仅影响您传递给Entry 调用的单个实体。使用新的上下文实例进行数据持久性后,您必须始终告知要保存的任何实体的 EF 状态。

    在您的情况下,您需要执行以下任一操作:

    ctx2.Items.Attach(fAp); // Now you informed context about existing entity
    fAp.Sections.Add(nsec); // Now you told both context and your item that new section exists
    

    或者这个(避免一些其他问题的首选解决方案):

    fAp.Sections.Add(nsec); // Your item is not tracked by the new context yet
    ctx2.Items.Add(fAp);     // Now context things that both item and section are new
    ctx2.Entry(fAp).State = EntityState.Unchanged; // Make sure that item is not inserted again
    

    或者这个:

    fAp.Sections.Add(nsec); // Your item is not tracked by the new context yet
    ctx2.Items.Attach(fAp); // Now context things that both item and section are existing
    ctx2.Entry(nsec).State = EntityState.Added; // Make sure that section is tracked as new 
    

    跟踪所有此类属性和设置将非常困难 他们的状态手动。这种情况下如何更新数据?

    您的代码正在执行这些更改,因此应该不会那么困难。您甚至可以为每个实体添加一些额外的属性,例如 IsDirty,并将其用作跟踪实体状态的助手。

    【讨论】:

    • 谢谢@Ladislav 我无法控制客户端如何更新我的对象。我已经创建了客户要求项目的存储库。然后再次调用 commit 方法。
    • 由于这里有篇幅限制,我已经在下面详细回复了...
    【解决方案2】:

    更新的实体

    public class Item
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Sections Sections { get; set; }
        public OtherInfo Info { get; set; }
    }
    public class OtherInfo
    {
        public Guid Id {get;set;}
        public string Description {get;set;}
    }
    

    在检索到 Item 对象后,用户可以修改任何内容。比如说上面给出的例子,用户只是插入一个新的 Section 并通过传递对象 Item 来调用 commit 方法。

    存储库类中的提交方法

    public void Commit(Item fAp)
    {
        using (var ct = new MyContext())
        {
            bool isExist = ct.Item.Any(a => a.Id == fAp.Id);
            if (!isExist)
            {
               ct.Items.Add(fAp);
            }
            else
            {
               ct.Items.Attach(fAp);
               ct.Entry(fAp).State = EntityState.Modified;
            }
            ct.SaveChanges();
        }
    }
    

    在提交方法中,我不会知道用户对对象做了什么。在我的测试中,我添加了部分并调用了这个方法。因为,OtherInfo 没有被修改,它的抛出错误表明 OtherInfo 表的主键重复。

    因此,为了使其正常工作,我必须更改所有实体的状态,例如

    部分 - 已添加;其他信息 - 无变化;物品 - 无变化

    由于结构有点复杂,要跟踪所有实体看起来很困难。

    这种方法(提交方法)是否可行或提出了更好的解决方案?

    【讨论】:

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