Entity framework 如何删除实体框架一对多关系中的子记录?

Entity framework 如何删除实体框架一对多关系中的子记录?,entity-framework,parent-child,Entity Framework,Parent Child,这个问题已经讨论/问了很多次,但我仍然找不到这个问题的最终答案。我为冗长的解释提前道歉,这对我来说意义重大,以至于你花时间阅读并理解我的问题 我正在为一个基于预订的多层商业实体开发一个停车场管理系统,在该系统中,客户致电系统管理员,让他们知道他们希望停车场可供使用的日期和时间跨度 对于要在系统中注册新停车场的人,他们首先必须注册并选择一个楼层。因此,楼层和停车场之间存在一对多的关系。一层可以有很多停车场。以下是我的类的实现方式: 地板类 public class Nivel { #reg

这个问题已经讨论/问了很多次,但我仍然找不到这个问题的最终答案。我为冗长的解释提前道歉,这对我来说意义重大,以至于你花时间阅读并理解我的问题

我正在为一个基于预订的多层商业实体开发一个停车场管理系统,在该系统中,客户致电系统管理员,让他们知道他们希望停车场可供使用的日期和时间跨度

对于要在系统中注册新停车场的人,他们首先必须注册并选择一个楼层。因此,楼层和停车场之间存在一对多的关系。一层可以有很多停车场。以下是我的类的实现方式:

地板类

public class Nivel
{
    #region Campos

    int _IdNivel;

    #endregion

    #region Propiedades

    public int IdNivel
    {
        get
        {
            return _IdNivel;
        }

        private set
        {
            _IdNivel = value;
        }
    }

    public string NombreNivel { get; set; }
    public int CantidadParqueos { get; set; }
    public List<Parqueo> Parqueos { get; set; }

    #endregion

    #region Constructores

    public Nivel()
    {
        Parqueos = new List<Parqueo>();
    }

    #endregion
}
public class Parqueo
{
    #region Campos

    int _IdParqueo;
    int _IdNivel;

    #endregion

    #region Propiedades

    public int IdParqueo
    {
        get
        {
            return _IdParqueo;
        }

        private set
        {
            _IdParqueo = value;
        }
    }

    public int IdNivel
    {
        get
        {
            return _IdNivel;
        }
        private set
        {
            _IdNivel = value;
        }
    }

    public string NombreParqueo { get; set; }
    public string EstadoParqueo { get; set; }
    public Nivel Nivel { get; set; }

    #endregion

    #region Constructores

    public Parqueo()
    {

    }

    public Parqueo(string NombreParqueo, string EstadoParqueo)
    {            
        this.NombreParqueo = NombreParqueo;
        this.EstadoParqueo = EstadoParqueo;
    }

    #endregion
}
现在这一切都很顺利。正如标题所说,我的主要问题与删除给定楼层上的特定停车场有关。我已经解决了这个问题,但我想要一个比我已有的更优雅的解决方案。这是我在表格中的停车场登记表的装载事件:

    private void FrmRegistroParqueos_Load(object sender, EventArgs e)
    {
        this.groupBox3.Left = (this.Parent.Width / 2) - (this.groupBox3.Width / 2);
        this.groupBox3.Top = (this.Parent.Height / 2) - (this.groupBox3.Height / 2);            

        contexto.Niveles.Include("Parqueos").Load();            

        bindingSourceNiveles.DataSource = contexto.Niveles.Local.ToBindingList<Nivel>();

        bindingSourceParqueos.DataSource = bindingSourceNiveles;

        bindingSourceParqueos.DataMember = "Parqueos";

        this.DgvNiveles.DataSource = bindingSourceNiveles;
        this.DgvParqueos.DataSource = bindingSourceParqueos;            

        if (this.DgvNiveles.Rows.Count > 0)
        {
            this.DgvNiveles.ClearSelection();
        }
    }
如果您熟悉实体框架,那么您已经知道一些背景信息。从一个导航对象集合中删除相关的子对象实际上并不会从数据库中删除该对象:它只是删除父对象和子对象之间的关系。现在,在我的代码中,一个楼层和一个停车场之间建立的关系是一个严格的关系——你不能在没有与特定楼层相关的情况下添加一个停车场。这意味着停车场表中的外键不可为空。因此,通过从楼层的停车场集合中删除程序中的特定停车场违反了此规则,然后上下文抛出一个异常,表示在对上下文调用方法SaveChanges时,我试图将外键设置为null

由于这种笨重的行为,在找到解决方案之前,我尝试了一种变通方法:如果实体框架不允许我通过程序中的对象删除子对象,那么我将通过从上下文本身删除它来删除它,这样更改将反映回我的停车场的datagridview,用户可以看到停车场确实被删除了。现在,这种方法“有效”,因为它确实从数据库中删除了子记录,但是停车场datagridview抛出了一个索引越界异常,表示在删除停车场的位置没有任何内容。这是一张例外情况的图片,我很抱歉不能在这里插入图片(我没有足够的声誉点数,这是我第一次在这里提问):

这个失败的解决方案的代码给了我一个严重的异常(顺便说一句,在点击messagebox上的ok按钮后会触发3次以上):

虽然失败了,但这正是我想在我的计划中实现的,在我看来,这是事情应该运行的方式。通过对象集合删除当前选定的对象并将其反射回数据库,或者从数据库中删除子记录并使其反射回对DataGridView的更改—这就是我想要的。但这一切都不起作用,第一个解决方案不起作用,因为我的外键中不能有空值,第二个解决方案是因为datagridview引发了异常。我只希望能够以用户可以在datagridview中实际看到的方式删除datagridview中的记录,并将其从数据库中删除

因此,我得出了目前的解决方案:

1-实例化一个新停车场

2-将其设置为停车场数据网格视图上当前选定的停车场

3-通过停车场的bindingsource RemoveCurrent()方法删除当前选定的对象,以便用户可以看到停车场已从datagridview中删除。此时,通过将该对象的外键属性设置为null,将上下文中的关系设置为删除

4-通过上下文的Remove()方法从数据库中删除已删除的记录

5-在上下文中调用SaveChanges()方法,以便从数据库中删除子记录

基本上,我所做的是删除对象两次,一次是为了让用户看到停车场在datagridview中被删除,另一次是从数据库中删除不相关的子对象(null外键)。这样,当我调用SaveChanges时,我可以避免上下文抱怨子记录有空外键

在我看来,这是一个丑陋的解决问题的办法,我想就任何其他方法,你可能对此有一些反馈。提前感谢您花时间阅读这面长长的文字墙,我很抱歉这么冗长,但我觉得有必要确切地理解正在发生的事情


祝你们过得愉快。

你们可以试着让一对多的关系成为一种认同的关系。您可以通过将外键
Parqueo.IdNivel
Parqueo
Nivel
包含到主键中来完成此操作。
Parqueo
的主键变为复合键,然后由
(IdParqueo,IdNivel)
组成

线索是,在标识关系中,为了删除依赖项,只需从
Nivel.Parqueos
集合中删除
Parqueo
。调用
SaveChanges
时,删除的
Parqueo
将自动从数据库中删除,从而避免违反外键约束。你会
    private void BtnEliminarParqueo_Click(object sender, EventArgs e)
    {
        ((Nivel)bindingSourceNiveles.Current).CantidadParqueos -= 1;

        bindingSourceNiveles.ResetCurrentItem();            

        Parqueo parqueoEliminado = new Parqueo();

        parqueoEliminado = (Parqueo)bindingSourceParqueos.Current;

        bindingSourceParqueos.RemoveCurrent();

        contexto.Parqueos.Remove(parqueoEliminado);

        contexto.SaveChanges();
    }
    private void BtnEliminarParqueo_Click(object sender, EventArgs e)
    {
        ((Nivel)bindingSourceNiveles.Current).CantidadParqueos -= 1;

        bindingSourceNiveles.ResetCurrentItem();            

        contexto.Parqueos.Remove((Parqueo)bindingSourceParqueos.Current);

        contexto.SaveChanges();
    }