这个问题已经讨论过很多次了,但我仍然找不到这个问题的确切答案。我为冗长的解释表示歉意,这对我来说意义重大,以至于你花时间阅读并理解了我的问题。
我正在为一个基于预订的多功能商业实体开发一个停车场管理系统,在这个系统中,客户给系统管理员打电话,让他们知道他们希望在什么时间和日期有一个停车场供他们使用。
如果有人要在系统中注册一个新的停车场,他们首先必须注册并选择一层楼。所以楼层和停车场之间有一对多的关系。一层可以有很多停车场。我的类就是这样实现的:
地板类
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();
}
}简而言之,这就是我正在做的事情:我正在实例化上下文以及两个绑定源,这样我就可以用两个datagridview实现一个主/详细表单,以便在其中一个数据视图上选择一个特定的楼层来填充另一个数据视图,并在该选定的楼层上使用可用的停车场填充另一个数据视图。然后,我对上下文运行一个查询,以便在本地内存(包括停车场)中加载数据库中的所有可用楼层,以便填充绑定源并将信息反映到数据视图中。
以下是我删除选定停车场的代码:
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时,我试图为外键设置一个null。
由于这种烦琐的行为,在找到解决方案之前,我尝试了一个解决方案:如果实体框架不允许我通过程序中的对象删除一个子对象,那么我会删除它,从上下文本身中删除它,这样就可以将更改反映回我的停车场的数据视图中,用户可以看到停车场确实被删除了。现在,这种方法“起作用了”,因为它确实从数据库中删除了子记录,但是停车场datagridview抛出了一个索引异常,表示在被删除的停车场的位置上什么都没有。这是一张例外的照片,我很抱歉没能在这里插入图片(我没有足够的声誉点,这是我第一次在这里提问):
https://www.dropbox.com/s/vank28utf7bpzdg/Exception.png?m
这个失败工作的代码给了我一个令人讨厌的异常(顺便说一下,在单击messagebox上的ok按钮之后,这个异常会触发3次以上):
private void BtnEliminarParqueo_Click(object sender, EventArgs e)
{
((Nivel)bindingSourceNiveles.Current).CantidadParqueos -= 1;
bindingSourceNiveles.ResetCurrentItem();
contexto.Parqueos.Remove((Parqueo)bindingSourceParqueos.Current);
contexto.SaveChanges();
}虽然失败了,但这正是我想在我的计划中实现的,在我看来,事情应该是这样的。通过对象集合删除当前选定的对象,并让它反映回数据库或从数据库中删除子记录,并让它反映对数据视图的更改--这就是我想要的。但是所有这些都不起作用,第一个解决方案不能工作,因为我的外键中不能有空值,第二个解决方案是因为datagridview抛出的异常。我只想能够删除我的datagridview中的记录,就像用户在datagridview中实际看到的那样,数据视图被删除了,并且也从数据库中删除了它。
于是,我找到了我目前的解决方案:
1 -实例化一个新的停车场
2 -将其设置为当前选定的停车场数据视图中的停车场
3 -通过停车场的bindingsource RemoveCurrent()方法删除当前选定的对象,以便用户可以看到停车场已从数据视图中删除。此时,通过将该对象的外键属性设置为null,关系将在上下文中被删除。
4 -通过上下文的()方法从数据库中删除已删除的记录
5 -调用上下文中的SaveChanges()方法,以便从数据库中删除子记录
基本上,我要做的是删除对象两次,一次,这样用户就可以看到数据视图中的停车场被删除了,并再次从数据库中删除了不相关的子对象(空外键)。这样,当我调用SaveChanges时,我可以防止上下文抱怨子记录有一个空外键。
在我看来,这是一个丑陋的解决问题的办法,我想要一些反馈意见,任何其他的方法,你可能对此。事先谢谢你抽出时间阅读这堵宽大的文字墙,我对你的发言表示歉意,但我觉得有必要确切地理解这是怎么回事。
祝大家今天过得愉快。
发布于 2013-03-13 17:53:25
你可以试着让一对多的关系成为一种可识别的关系。要做到这一点,可以将外键Parqueo.IdNivel (从Parqueo到Nivel )包含到主键中。Parqueo的主键变为复合密钥,然后由(IdParqueo, IdNivel)组成。
提示是,在标识关系中,为了删除依赖项,只需从Parqueo集合中删除Nivel.Parqueos。当您调用SaveChanges时,删除的Parqueo将自动从数据库中删除,从而避免了外键约束冲突。您不必调用contexto.Parqueos.Remove(parqueo)从数据库中删除它。
有关识别关系的更多详细信息和示例如下:
https://stackoverflow.com/questions/15388829
复制相似问题