WPF C#应用插入数据库时FOREIGN KEY约束冲突问题
问题分析
你遇到的外键冲突,核心原因是插入VotacaoValores时使用的JuriID在Juri表中不存在。具体问题点:
Juri表的ID是自增字段(IDENTITY(1,1)),但你在LerDadosFicheiroValores中直接用IDJuri + j生成JuriID,这个值和数据库实际生成的Juri记录ID不匹配。- 同理,
Candidato的IDCandidato + i也存在相同风险,因为Candidato的ID也是自增的,不能假设起始ID是传入的IDCandidato。 LerDadosFicheiroCandidato和LerDadosFicheiroValores使用字符串拼接生成SQL语句,存在SQL注入风险,也容易引发格式错误。
解决方案
1. 保存插入Juri和Candidato时的实际ID
修改插入函数,获取数据库生成的自增ID并存储,后续传入LerDadosFicheiroValores使用。
修改后的LerDadosFicheiroJuri:
private List<int> LerDadosFicheiroJuri(string CaminhoFicheiro, int id) { var juriIds = new List<int>(); using (var rf = new StreamReader(CaminhoFicheiro)) { string stringComando = "INSERT INTO Juri(Nome,Sobrenome,VotacaoID) OUTPUT INSERTED.ID values(@Nome, @Sobrenome, @VotacaoID)"; using (var comando = new SqlCommand(stringComando, baseDeDados)) { comando.Parameters.Add("@Nome", SqlDbType.VarChar, 60); comando.Parameters.Add("@Sobrenome", SqlDbType.VarChar, 60); comando.Parameters.AddWithValue("@VotacaoID", id); while (!rf.EndOfStream) { var line = rf.ReadLine(); string[] campos = line.Split(','); comando.Parameters["@Nome"].Value = campos[0]; comando.Parameters["@Sobrenome"].Value = campos[1]; // 获取插入后生成的自增ID int juriId = (int)comando.ExecuteScalar(); juriIds.Add(juriId); } } } return juriIds; }
修改后的LerDadosFicheiroCandidato:
private List<int> LerDadosFicheiroCandidato(string CaminhoFicheiro, int id) { var candidatoIds = new List<int>(); using (var rf = new StreamReader(CaminhoFicheiro)) { // 先验证Votacao是否存在 string checkVotacao = "SELECT 1 FROM Votacao WHERE ID = @VotacaoID"; using (var checkCmd = new SqlCommand(checkVotacao, baseDeDados)) { checkCmd.Parameters.AddWithValue("@VotacaoID", id); var result = checkCmd.ExecuteScalar(); if (result == null) return candidatoIds; } string insertCmd = "INSERT INTO Candidato(Nome,Sobrenome,VotacaoID,Idade) OUTPUT INSERTED.ID values(@Nome, @Sobrenome, @VotacaoID, @Idade)"; using (var comando = new SqlCommand(insertCmd, baseDeDados)) { comando.Parameters.Add("@Nome", SqlDbType.VarChar, 60); comando.Parameters.Add("@Sobrenome", SqlDbType.VarChar, 60); comando.Parameters.AddWithValue("@VotacaoID", id); comando.Parameters.Add("@Idade", SqlDbType.Int); while (!rf.EndOfStream) { var line = rf.ReadLine(); string[] campos = line.Split(','); comando.Parameters["@Nome"].Value = campos[0]; comando.Parameters["@Sobrenome"].Value = campos[1]; comando.Parameters["@Idade"].Value = Convert.ToInt32(campos[2]); // 获取插入后生成的自增ID int candidatoId = (int)comando.ExecuteScalar(); candidatoIds.Add(candidatoId); } } } return candidatoIds; }
2. 修改LerDadosFicheiroValores使用实际ID
不再依赖传入的起始ID,直接使用上述函数返回的真实ID列表:
private void LerDadosFicheiroValores(string CaminhoFicheiro, List<int> juriIds, List<int> candidatoIds) { using (var rf = new StreamReader(CaminhoFicheiro)) { // 验证Votacao存在(可选) string checkVotacao = "SELECT 1 FROM Votacao"; using (var checkCmd = new SqlCommand(checkVotacao, baseDeDados)) { var result = checkCmd.ExecuteScalar(); if (result == null) return; } string insertCmd = "INSERT INTO VotacaoValores(JuriID,Valor,Pontuacao,CandidatoID) values(@JuriID, @Valor, @Pontuacao, @CandidatoID)"; using (var comando = new SqlCommand(insertCmd, baseDeDados)) { comando.Parameters.Add("@JuriID", SqlDbType.Int); comando.Parameters.Add("@Valor", SqlDbType.Int); comando.Parameters.Add("@Pontuacao", SqlDbType.Int); comando.Parameters.Add("@CandidatoID", SqlDbType.Int); while (!rf.EndOfStream) { var line = rf.ReadLine(); string[] campos = line.Split(','); for (int j = 0; j < juriIds.Count; j++) { for (int i = 0; i < candidatoIds.Count; i++) { comando.Parameters["@JuriID"].Value = juriIds[j]; comando.Parameters["@Valor"].Value = Convert.ToInt32(campos[2 + i]); comando.Parameters["@Pontuacao"].Value = Convert.ToInt32(campos[9 + i]); comando.Parameters["@CandidatoID"].Value = candidatoIds[i]; comando.ExecuteNonQuery(); } } } } } }
3. 调用逻辑修改
先插入Juri和Candidato获取ID列表,再传入值插入函数:
// 假设votacaoId是当前操作的VotacaoID var juriIds = LerDadosFicheiroJuri("你的Juri文件路径", votacaoId); var candidatoIds = LerDadosFicheiroCandidato("你的Candidato文件路径", votacaoId); LerDadosFicheiroValores("你的Valores文件路径", juriIds, candidatoIds);
额外优化点
- 移除了手动
rf.Close(),using块会自动释放资源。 - 所有SQL操作改为参数化查询,彻底避免SQL注入风险。
- 增加了数据存在性检查,避免无效插入操作。
内容的提问来源于stack exchange,提问作者jose soeiro
相关产品推荐
相关产品推荐

