实体框架不保存修改后的孩子 [英] Entity Framework not saving modified children

查看:82
本文介绍了实体框架不保存修改后的孩子的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

沮丧,这个。这是一个由数据库优先实体框架生成的一对相关对象:

  public partial class DevelopmentType 
{
public DevelopmentType()
{
this.DefaultCharges = new HashSet< DefaultCharge>();
}

public System.Guid RowId {get;组; }
public string Type {get;组; }

public virtual ICollection< DefaultCharge> DefaultCharges {get;组; }
}

public partial class DefaultCharge
{
public System.Guid RowId {get;组; }
public decimal ChargeableRate {get;组; }
public Nullable< System.Guid> DevelopmentType_RowId {get;组; }

public virtual DevelopmentType DevelopmentType {get;组; }
}

这是我打电话来保存DevelopmentType的代码 - 它涉及到自动分类器,因为我们区分实体对象与DTO:

  public void SaveDevelopmentType(DevelopmentType_dto dt)
{
实体.DevelopmentType mappedDevType = Mapper.Map< DevelopmentType_dto,Entities.DevelopmentType>(dt);
_Context.Entry(mappedDevType).State = System.Data.EntityState.Modified;

_Context.DevelopmentTypes.Attach(mappedDevType);
_Context.SaveChanges();
}

在我的用户界面中,最常见的操作是让用户看在一个DevelopmentTypes列表,并更新他们的DefaultCharge。所以当我使用上面的代码测试它,它运行没有错误,但没有任何实际的变化。



如果我暂停在调试器,很明显,更改的DefaultCharge正在传递进入该函数,并将其附加到要保存的DevelopmentType。



如果我在visual studio中手动更改值,那么 保存更新的值。这是更令人困惑的。



使用SQL Server Profiler监控数据库显示更新命令仅针对父对象 发布 >



我有其他类似的代码可以按预期的方式运行。我在这里做错了什么?



编辑:



我发现如果你在调用SaveDevelopmentType:

  using(TransactionScope scope = new TransactionScope())
{
dt.Type =Test1;
dt.DefaultCharges.First()。ChargeableRate = 99;
_CILRepository.SaveDevelopmentType(dt);
scope.Complete();
}

更改为类型保存,但对ChargeableRate的更改不会。我不认为它有帮助,但是以为我会添加它。

解决方案

问题是,EF是不知道更改的DefaultCharges。



通过将 DevelopmentType 的状态设置为 EntityState.Modified ,EF只知道对象 DevelopmentType 已被更改。但是,这意味着EF只会更新 DevelopmentType ,但不是导航属性。



解决方法 - 这不是最佳实践 - 将迭代当前<$的$ DefaultCharge c $ c> DevelopmentType 并将实体状态设置为 EntityState.Modified



此外,我建议先把实体附加到上下文中,然后再改变状态。



评论后编辑



正如使用DTO一样,我想通过不同的层或不同的机器传输这些对象。



在这种情况下,我建议使用自我跟踪实体,因为不可能共享一个上下文。这些实体另外保存其当前状态(即新,更新,删除等)。关于自我跟踪实体,网络上有许多教程。



例如。 MSDN - 使用自我跟踪实体


Frustrating, this. Here's a pair of related objects, as generated by database-first Entity Framework:

public partial class DevelopmentType
{
    public DevelopmentType()
    {
        this.DefaultCharges = new HashSet<DefaultCharge>();
    }

    public System.Guid RowId { get; set; }
    public string Type { get; set; }

    public virtual ICollection<DefaultCharge> DefaultCharges { get; set; }
}

public partial class DefaultCharge
{
    public System.Guid RowId { get; set; }
    public decimal ChargeableRate { get; set; }
    public Nullable<System.Guid> DevelopmentType_RowId { get; set; }

    public virtual DevelopmentType DevelopmentType { get; set; }
}

Here's the code that I'm calling to save a DevelopmentType - it involves automapper since we differentiate entity objects from DTOs:

    public void SaveDevelopmentType(DevelopmentType_dto dt)
    {
        Entities.DevelopmentType mappedDevType = Mapper.Map<DevelopmentType_dto, Entities.DevelopmentType>(dt);
        _Context.Entry(mappedDevType).State = System.Data.EntityState.Modified;

        _Context.DevelopmentTypes.Attach(mappedDevType);
        _Context.SaveChanges();
    }

In my user interface, the most common operation will be for a user to look at a list of DevelopmentTypes and update their DefaultCharge. So when I test this using the above code, it runs without error, but nothing actually changes.

If I pause in the debugger it's clear that the changed DefaultCharge is being passed into the function, and that it's attached to the DevelopmentType to be saved.

Stepping through it, if I change the value manually inside visual studio, it does save the updated value. Which is just even more confusing.

Monitoring the database with SQL Server Profiler reveals that update commands are issued only for the parent object and not for any attached objects.

I have other similar code elsewhere that functions as expected. What am I doing wrong here?

EDIT:

I have discovered that if you do this prior to the call to SaveDevelopmentType:

        using (TransactionScope scope = new TransactionScope())
        {
            dt.Type = "Test1";
            dt.DefaultCharges.First().ChargeableRate = 99;
            _CILRepository.SaveDevelopmentType(dt);
            scope.Complete();
        }

The change to Type saves, but the change to ChargeableRate does not. I don't think it helps, massively, but thought I'd add it.

解决方案

The problem is, that EF is not aware of the changed DefaultCharges.

By setting the State of the DevelopmentType to EntityState.Modified, EF only knows that the object DevelopmentType has been changed. However, this means that EF will only update DevelopmentType but not it's navigation properties.

A workaround - which isn't best practice - would be to iterate over all DefaultCharge of the current DevelopmentType and set the entity state to EntityState.Modified.

Additionally I would recommend to attach the entity to the context first, and change the state afterwards.

EDIT after comment

As you are using DTOs I suppose you are transfering these objects either through different layers or different machines.

In this case I would recommend to use self tracking entities, because it is not possible to share one context. These entities additionally holds their current state (ie. new, updated, deleted etc). There are many tutorials on the net about self tracking entities.

e.g. MSDN - Working with Self-Tracking Entities

这篇关于实体框架不保存修改后的孩子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆