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

C#操作SQL单条企业信息:插入更新报错及方案咨询

问题描述

在Visual Studio 2019中设计企业信息表单,需将唯一的企业信息保存到SQL表的单条记录中。采用先查询表中id为1的记录,根据是否存在及CompanyName是否为空来决定执行插入或更新操作,但运行时出现错误:System.IndexOutOfRangeException: 'CompanyName'。相关C#代码如下:

private void btnSave_Click(object sender, EventArgs e)
{
    cmd = new SqlCommand("select * from CompanyProfile where id=@N", con);
    cmd.Parameters.AddWithValue("@N", "1");
    if (cmd.Connection.State == ConnectionState.Open)
    {
        cmd.Connection.Close();
    }
    con.Open();
    dr = cmd.ExecuteReader();
    if (dr.Read())
    {
        if (dr["CompanyName"].ToString() == null)
        {
            con.Close();
            SqlCommand cmd = new SqlCommand("insert into CompanyProfile(ComanyName,RegNo,RegDate,NID,EID,WorkShopCode,WebSite,CEOName,Tel,Mobile,Fax,ZipCode,Email,Address,Logo)values(@a,@b,@c,@d,@e,@f,@g,@h,@i,@j,@k,@l,@m,@n,@o)", con);
            if (PBLogo.Image == null)
            {
                MessageBox.Show("please insert PIC");
                return;
            }
            if (cmd.Connection.State == ConnectionState.Open)
            {
                cmd.Connection.Close();
            }
            con.Open();
            byte[] ar = File.ReadAllBytes(PBLogo.ImageLocation);

            cmd.Parameters.AddWithValue("@a", txtCompanyName.Text);
            cmd.Parameters.AddWithValue("@b", txtRegNo.Text);
            cmd.Parameters.AddWithValue("@c", txtRegDate.Text);
            cmd.Parameters.AddWithValue("@d", txtNID.Text);
            cmd.Parameters.AddWithValue("@e", txtEID.Text);
            cmd.Parameters.AddWithValue("@f", txtWorkShopCode.Text);
            cmd.Parameters.AddWithValue("@g", txtWebSite.Text);
            cmd.Parameters.AddWithValue("@h", txtCEOName.Text);
            cmd.Parameters.AddWithValue("@i", txtTel.Text);
            cmd.Parameters.AddWithValue("@j", txtMobile.Text);
            cmd.Parameters.AddWithValue("@k", txtFax.Text);
            cmd.Parameters.AddWithValue("@l", txtZipCode.Text);
            cmd.Parameters.AddWithValue("@m", txtEmail.Text);
            cmd.Parameters.AddWithValue("@n", txtAddress.Text);
            cmd.Parameters.AddWithValue("@o", SqlDbType.VarBinary).Value = ar;
            cmd.ExecuteNonQuery();
            con.Close();
            MessageBox.Show("form saved");
        }
        else
        {
            con.Close();
            MemoryStream ms1 = new MemoryStream();
            try
            {
                byte[] arrPic = ms1.GetBuffer();
                ms1.Close();
                cmd.Parameters.Clear();
                cmd.Connection = con;
                string Updatetable = "Update CompanyProfile set CompanyName='" + txtCompanyName.Text + "',RegNo='" + txtRegNo.Text + "',RegDate='" + txtCompanyName.Text + "',NID='" + txtNID.Text + "',EID='" + txtEID.Text + "',WorkShopCode='" + txtWorkShopCode.Text + "',Website='" + txtWebSite.Text + "',CEOName='" + txtCEOName.Text + "',Tel='" + txtTel.Text + "',Mobile='" + txtMobile.Text + "',Fax='" + txtFax.Text + "',ZipCode='" + txtZipCode.Text + "',Email='" + txtEmail.Text + "',Address='" + txtAddress.Text + "',Pic=@Pic where id=@Y";

                cmd.Parameters.AddWithValue("@Y", "1");
                SqlCommand com = new SqlCommand(Updatetable, con);
                com.Parameters.AddWithValue("@Pic", arrPic);
                con.Open();
                com.ExecuteNonQuery();
                con.Close();
                MessageBox.Show("form updated");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}

咨询两个问题:

  1. 该错误的原因是什么,如何修复?
  2. 当前这种实现单条记录插入或更新的方式是否合适?

解答

1. 错误原因及修复方案

错误原因

System.IndexOutOfRangeException: 'CompanyName' 本质是DataReader中找不到指定的CompanyName列,具体诱因包括:

  • 字段拼写错误:插入语句中写的是ComanyName(少了一个字母p),如果数据库表实际字段是CompanyName,会导致插入时未给目标字段赋值,后续查询时DataReader虽包含CompanyName列,但逻辑判断错误;若表中被误创建ComanyName字段,代码里用dr["CompanyName"]自然找不到对应列。
  • NULL判断逻辑错误:直接用dr["CompanyName"].ToString() == null判断NULL值不成立——即使字段为NULL,ToString()会返回空字符串而非null,且若字段不存在,直接访问dr["CompanyName"]会触发索引越界。
  • 字段名大小写/不一致:更新语句中写的是Website,但插入语句用的是WebSite,若数据库采用区分大小写的排序规则,会导致列名不匹配。

修复步骤

  1. 修正字段拼写:将插入语句中的ComanyName改为CompanyName,更新语句中的Website改为WebSite,确保与数据库表字段名完全一致;
  2. 修正NULL判断逻辑:替换原判断为if (dr.IsDBNull(dr.GetOrdinal("CompanyName")) || string.IsNullOrWhiteSpace(dr["CompanyName"].ToString())),先判断字段是否为NULL,再判断内容是否为空;
  3. 明确查询列:将查询语句从select *改为select CompanyName from CompanyProfile where id=@N,只返回需要的列,避免不必要的列干扰;
  4. 避免全局资源泄漏:不要使用全局的SqlCommand、SqlDataReader和SqlConnection,在方法内声明并使用using语句自动释放资源。

2. 当前实现方式的合理性及优化方案

当前方式的问题

  • 并发风险:先查询后操作的逻辑不是原子性的,多用户同时操作时可能出现重复插入;
  • SQL注入漏洞:更新语句直接拼接字符串(如CompanyName='" + txtCompanyName.Text + "'),存在严重的注入风险;
  • 资源泄漏:全局连接、命令未正确释放,易导致数据库连接池耗尽;
  • 图片处理错误:更新时的MemoryStream未读取实际图片内容,导致更新的图片为空;
  • 逻辑冗余:手动处理查询、插入、更新分支,代码繁琐易出错。

更优实现方案

使用SQL Server的MERGE语句,在数据库层面完成原子性的插入/更新操作,简化代码同时避免并发问题:

优化后代码示例

private void btnSave_Click(object sender, EventArgs e)
{
    if (PBLogo.Image == null)
    {
        MessageBox.Show("please insert PIC");
        return;
    }

    // 正确转换图片为字节数组
    byte[] logoBytes = null;
    using (var ms = new MemoryStream())
    {
        PBLogo.Image.Save(ms, PBLogo.Image.RawFormat);
        logoBytes = ms.ToArray();
    }

    // 原子性插入/更新的MERGE语句
    string mergeSql = @"
        MERGE INTO CompanyProfile AS Target
        USING (SELECT @Id AS Id) AS Source
        ON Target.Id = Source.Id
        WHEN MATCHED THEN
            UPDATE SET 
                CompanyName = @CompanyName,
                RegNo = @RegNo,
                RegDate = @RegDate,
                NID = @NID,
                EID = @EID,
                WorkShopCode = @WorkShopCode,
                WebSite = @WebSite,
                CEOName = @CEOName,
                Tel = @Tel,
                Mobile = @Mobile,
                Fax = @Fax,
                ZipCode = @ZipCode,
                Email = @Email,
                Address = @Address,
                Logo = @Logo
        WHEN NOT MATCHED THEN
            INSERT (Id, CompanyName, RegNo, RegDate, NID, EID, WorkShopCode, WebSite, CEOName, Tel, Mobile, Fax, ZipCode, Email, Address, Logo)
            VALUES (@Id, @CompanyName, @RegNo, @RegDate, @NID, @EID, @WorkShopCode, @WebSite, @CEOName, @Tel, @Mobile, @Fax, @ZipCode, @Email, @Address, @Logo);
    ";

    // 使用using自动释放资源
    using (var con = new SqlConnection("你的数据库连接字符串"))
    using (var cmd = new SqlCommand(mergeSql, con))
    {
        cmd.Parameters.AddWithValue("@Id", 1);
        cmd.Parameters.AddWithValue("@CompanyName", txtCompanyName.Text);
        cmd.Parameters.AddWithValue("@RegNo", txtRegNo.Text);
        cmd.Parameters.AddWithValue("@RegDate", txtRegDate.Text);
        cmd.Parameters.AddWithValue("@NID", txtNID.Text);
        cmd.Parameters.AddWithValue("@EID", txtEID.Text);
        cmd.Parameters.AddWithValue("@WorkShopCode", txtWorkShopCode.Text);
        cmd.Parameters.AddWithValue("@WebSite", txtWebSite.Text);
        cmd.Parameters.AddWithValue("@CEOName", txtCEOName.Text);
        cmd.Parameters.AddWithValue("@Tel", txtTel.Text);
        cmd.Parameters.AddWithValue("@Mobile", txtMobile.Text);
        cmd.Parameters.AddWithValue("@Fax", txtFax.Text);
        cmd.Parameters.AddWithValue("@ZipCode", txtZipCode.Text);
        cmd.Parameters.AddWithValue("@Email", txtEmail.Text);
        cmd.Parameters.AddWithValue("@Address", txtAddress.Text);
        cmd.Parameters.Add("@Logo", SqlDbType.VarBinary).Value = logoBytes;

        try
        {
            con.Open();
            cmd.ExecuteNonQuery();
            MessageBox.Show("操作成功");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
}

优化点说明

  • 原子操作:MERGE语句在数据库层面完成判断和操作,避免并发冲突;
  • 防SQL注入:全参数化查询,无字符串拼接;
  • 资源自动释放:using语句自动回收数据库连接和命令,避免泄漏;
  • 逻辑简化:无需手动处理查询分支,代码更简洁;
  • 正确处理图片:将控件中的图片正确转换为字节数组,避免空值问题。

内容的提问来源于stack exchange,提问作者farhadsph

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 06:20:41