1

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

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

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

楼层类

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();
        }
    }

简而言之,这就是我正在做的事情:我正在实例化上下文以及两个绑定源,以便我可以使用两个 datagridviews 实现主/详细信息表单,以便在其中一个 datagridviews 上选择特定楼层来填充另一个datagridview 与所选楼层上的可用停车场。然后,我对上下文运行查询,以便我可以将数据库中所有可用的楼层加载到本地内存中,包括停车场,以便填充绑定源并将信息反映到 datagridviews。

这是我删除选定停车场的代码:

    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();
    }

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

由于这种繁琐的行为,我在找到我的解决方案之前尝试了一种解决方法:如果实体框架不允许我通过程序中的对象删除子对象,那么我会通过从上下文本身中删除它来删除它,这样然后这些更改会反映到我的停车场的 datagridview 中,用户可以看到停车场确实被移除了。现在,这种方法“有效”,因为它确实确实从数据库中删除了子记录,但是停车场 datagridview 抛出一个索引越界异常,说在删除的停车场的位置没有任何东西曾是。这是异常的图片,我很抱歉无法在此处插入图片(我没有足够的声望点,这是我第一次在这里提问):

https://www.dropbox.com/s/vank28utf7bpzdg/Exception.png?m

这个失败的解决方法的代码给了我一个讨厌的异常(顺便说一下,在单击消息框上的确定按钮后触发超过 3 次):

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

        bindingSourceNiveles.ResetCurrentItem();            

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

        contexto.SaveChanges();
    }

虽然失败了,但这是我想在我的程序中实现的,在我看来,事情应该是这样的。通过对象集合删除当前选定的对象并将其反映回数据库或从数据库中删除子记录并将其反映回对datagridviews的更改-这就是我想要的。但这些都不起作用,第一个解决方案不起作用,因为我的外键中不能有空值,第二个解决方案是因为 datagridview 抛出异常。我只想能够以用户可以在datagridview中实际看到的方式删除我的datagridview中的一条记录,并从数据库中删除它。

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

1 - 实例化一个新的停车场

2 - 将其设置为停车场datagridview上当前选择的停车场

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

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

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

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

在我看来,这是一个丑陋的问题解决方案,我想就您可能对此采取的任何其他方法提供一些反馈。预先感谢您花时间阅读这长篇文字墙,我很抱歉如此冗长,但我觉得有必要准确了解发生了什么。

祝你们有美好的一天。

4

1 回答 1

0

您可以尝试使一对多关系成为识别关系。您可以通过将外键Parqueo.IdNivelfrom包含ParqueoNivel主键中来做到这一点。的主键Parqueo成为复合键,然后由 组成(IdParqueo, IdNivel)

线索是,在识别关系中,为了删除依赖项,您只需ParqueoNivel.Parqueos集合中删除 。当你调用SaveChangesremovedParqueo时会自动从数据库中删除,从而避免外键约束违反。您无需调用contexto.Parqueos.Remove(parqueo)即可将其从数据库中删除。

有关识别关系的更多详细信息和示例如下:

https://stackoverflow.com/a/11033988/270591

于 2013-03-13T17:53:25.847 回答