从一对多关系中删除子对象

时间:2018-01-19 20:12:11

标签: c# entity-framework

(代码已简化)

背景说明:

我有一个父类:

namespace Project_Name.Models
{
    public class ParentRecord
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid Id { get; set; }

        public virtual ICollection<ChildDetail> ChildDetails { get; set; }
    }
}

和一个儿童班:

namespace Project_Name.Models
{
    public class ChildDetail
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid Id { get; set; }

        public string Value { get; set; }

        public Guid ParentRecord_Id { get; set; }

        [ForeignKey("ParentRecord_Id")]
        public ParentRecord ParentRecord { get; set; }
    }
}

我还有名称为<modelName>ViewModel的ViewModel和几乎完全相同的结构,但没有数据注释,而virtual ICollection只是List

他们一起形成一对多的关系。 ParentRecord可以有多个ChildDetailsChildDetail必须与ParentRecord相关联。

T-SQL表结构如下:

dbo.ParentRecord
+-----------+
|    Id     |
+-----------+
| <cr_guid> |
+-----------+

dbo.ChildDetail
+-----------+-----------+-----------------+
|    Id     |   Value   | ParentRecord_Id |
+-----------+-----------+-----------------+
| <cd_guid> | Some Text | <cr_guid>       |
+-----------+-----------+-----------------+

我的问题:

我需要能够根据我的ViewModel更新,添加和/或删除这些子对象。无论它有什么,我想成为最终的结果。

例如,我首先让3 ChildDetailsParentRecord相关,然后在我的视图中更新第1 ChildDetail,删除第2(或将值留空) ,并留下第三个。

我希望我的最终结果是ParentRecord剩下两个ChildDetails,第一个和第三个,第一个更新。

到目前为止我做了什么:

我可以添加和更新ChildDetails这样做,但它不能处理任何删除:

// ... code related to UnrelatedGranParent model
ParentRecord parentRecord = context.ParentRecords.FirstOrDefault(x => 
   x.Id == UnrelatedGrandParentViewModel.ParentViewModel.Id)
// ... code regarding ParentRecord's other not-shown-here properties

// Add or update each Child Detail from the View Model
foreach (var childDetailViewModel in ParentRecordViewModel
    .ChildDetailViewModels
    .Where(x => !x.Value.IsNullOrWhiteSpace()))
{
    // Map the Child Detail View Model to a Model
    var childDetail =
        Mapper.Map<ChildDetail>(childDetailViewModel);
    // Set the ParentRecord_Id property, since that doesn't get mapped
    childDetail.ParentRecord_Id = parentRecord.Id;
    // Add or update each Child Detail
    context.ChildDetails.AddOrUpdate(childDetail);
}

context.ParentRecord.AddOrUpdate(parentRecord);
context.SaveChanges();

所以我希望parentRecord.ChildDetails设置为传入的任何内容,假设它不为空或空格。
所以我必须删除其他所有内容。

我已尝试循环浏览每个现有ChildDetail并在其上使用.Remove()或设置context.Entry(childDetail).State = EntityState.DeletedEntityState.Detached,然后循环显示来自的值视图模型将它们设置为EntityState.AddedEntityState.Modified,但在我改变状态或保存更改时,仍然会抛出错误。

我也可以将.Where(x => !x.Value.IsNullOrWhiteSpace())移动到foreach循环中:

// map the childDetail
if (childDetail.Value.IsNullOrWhiteSpace())
{
    context.Entry(childDetail).State = EntityState.Deleted;
    continue;
}
context.ChildDetails.AddOrUpdate(childDetail);

但这并不能解释那些根本没有传入的人,而且我不知道直接设置EntityStates是否是一个好习惯。

1 个答案:

答案 0 :(得分:0)

感谢DevilSuichiro的评论,我能够找到这个简单的解决方案,我只需检查上下文中的每个ChildDetail是否存在于ViewModel的ChildDetails列表中,如果不存在:delete它!

// Add or update each Child Detail from the View Model
foreach (var childDetailViewModel in callDetailsViewModel.ParentRecordViewModel
    .ChildDetailViewModels)
{
    // Map the Child Detail View Model to a Model
    var childDetail =
        Mapper.Map<ChildDetail>(childDetailViewModel);
    // Set the ParentRecord_Id property, since that doesn't get mapped
    childDetail.ParentRecord_Id = parentRecord.Id;
    // Delete those that are null or whitespace
    if (childDetail.Value.IsNullOrWhiteSpace())
    {
        context.Entry(childDetail).State = EntityState.Deleted;
        continue;
    }

    // Add or update each Child Detail
    context.ChildDetails.AddOrUpdate(childDetail);
}

// For each contextChildDetail in the context, where they have matching Parent Record Id...
foreach (var contextChildDetail in context.ChildDetail.Where(x => x.ParentRecord_Id == parentRecord.Id))
{
    // ... and if we can't find it in the ViewModel list, then delete it
    if (callDetailsViewModel.ParentRecordViewModel.ChildDetailViewModels.FirstOrDefault(
            x => x.Id == contextChildDetail.Id) == null)
    {
        context.ChildDetail.Remove(contextChildDetail);
    }
}