You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 12:57:38