代码优先不是更新子对象

时间:2012-12-27 20:45:14

标签: c# entity-framework ef-code-first

我在使用CodeFirst更新POCO的子对象时遇到问题。我的POCO是以下

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

    public virtual Address Address { get; set; }
}

public class Address
{
    public int ID { get; set; 
    public string AddressLine { get; set; }
    public string City { get; set; }

    public virtual State State { get; set; }
}

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

我可以编辑地点及其子项的所有字段。除了作为DropDownList的State之外,所有属性都是文本框。

当用户单击“保存”按钮时,视图将返回所有已填充的“地方”属性,但只有ID填充的状态除外,因为它的值来自DropDownList且名称为空。

在编辑帖子方法中,我有以下代码:

if (ModelState.IsValid)
{
    bool isNewPlace = place.ID == -1;
    //Hack, State name is empty from View, we reload
    place.Address.State = new StateBLL().GetByID(place.Address.State.ID);
    new PlaceBLL().Update(place);

    return RedirectToAction("Index");
}

来自PlaceBLL类的更新代码如下

protected override void Update(Place place)
{
    MyDbContext.Instance().Set<Address>().Attach(place.Address);
    MyDbContext.Instance().Entry(place.Address).State = System.Data.EntityState.Modified;
    MyDbContext.Instance().Set<State>().Attach(place.Address.State);
    MyDbContext.Instance().Entry(place.Address.State) = System.Data.EntityState.Modified;
    MyDbContext.Instance().SaveChanges();
    }

当用户编辑Place对象时,除状态外所有字段都正确更新,当用户更改某个地方的状态时,此更改不会持久保存到数据库,如果看起来代码首先未检测到用户的状态更改。

你知道为什么代码首先没有检测到Place的状态变化值吗?

感谢。

2 个答案:

答案 0 :(得分:1)

您的Update方法更新了Address的(标量属性)并更新了State的(标量属性),但它没有更新关系 placeAddress之间(这不是问题,因为您显然只在视图中编辑Address属性而未将另一个Address实体分配给place)和它不会更新place.Addressplace.Address.State之间的关系。但这是你在视野中实际改变的。

要更新place.Addressplace.Address.State之间的关系,您有两种选择:

  • 在您的Address类中引入一个外键属性,表示该状态的FK(此类关联称为外键关联):

    public class Address
    {
        public int ID { get; set; 
        public string AddressLine { get; set; }
        public string City { get; set; }
    
        public int StateID { get; set; }
        public virtual State State { get; set; }
    }
    

    然后确保将下拉列表绑定到place.Address.StateID(而不是place.Address.State.ID)。将视图发布到服务器时,将从下拉列表中使用新选择的ID填充FK属性StateID。在Update方法中,您可以删除第三行和第四行。仅更新Address也将更新StateID外键。您还可以删除在编辑帖子操作中加载State的代码。

  • 如果要保留当前的独立关联(=在模型类中没有公开外键属性的关联),则必须从数据库加载原始状态并使用新的更新它状态:

    protected override void Update(Place place)
    {
        var dbPlace = MyDbContext.Instance().Places.Include(p => p.Address.State)
            .Single(p => p.ID == place.ID);
    
        // Update scalar properties of place
        MyDbContext.Instance().Entry(dbPlace)
            .CurrentValues.SetValues(place);
    
        // Update scalar properties of Address
        MyDbContext.Instance().Entry(dbPlace.Address)
            .CurrentValues.SetValues(place.Address);
    
        // Change relationship between Adress and State
        if (dbPlace.Address.State.ID != place.Address.State.ID)
        {
            MyDbContext.Instance().States.Attach(place.Address.State);
            dbPlace.Address.State = place.Address.State;
        }
    
        MyDbContext.Instance().SaveChanges();
    }
    

    同样,您可以删除在编辑帖子操作中加载State的代码,因为正确的键属性(State.ID)足以改变关系。

答案 1 :(得分:0)

您还需要附加实体。以下是您需要做的简单版本。

var updated = _dbSet.Attach(item);
_dataContext.Entry(item).State = EntityState.Modified;
return updated;