在将 BLOB 更新到 MySQL 期间遇到致命错误。

5
String query = "";
        string constr = ConfigurationSettings.AppSettings["MySQLConnectionStringForIMS"];
        using (MySqlConnection con = new MySqlConnection(constr))
        {
            //string query = "INSERT INTO user(name, files,contentType) VALUES (@name,@files,@contentType)";
            if (update == "mainSec")
            {
                query = "update main_section set contentType=@contentType,fileData=@fileData,fileNameAfterUploading=@fname,haveDir=@dir where id=@id";
            }
            else
            {
                query = "update sub_section set subContentType=@contentType,subFileData=@fileData,fileNameAfterUploading=@fname,haveDir=@dir where MainSecId=@id and id=@subId";
            }
            using (MySqlCommand cmd = new MySqlCommand(query))
            {
                cmd.Connection = con;
                cmd.CommandType = CommandType.Text;
                cmd.Parameters.AddWithValue("@contentType", contentType);
                cmd.Parameters.AddWithValue("@fileData", data);
                cmd.Parameters.AddWithValue("@fname", filename);
                cmd.Parameters.AddWithValue("@dir", 1);
                cmd.Parameters.AddWithValue("@id", mainId);
                if (update == "subSec")
                {
                    cmd.Parameters.AddWithValue("@subId", subId);
                }
                con.Open();
                int st = cmd.ExecuteNonQuery();
                if (st == 1)
                {
                    //Uri uri = new Uri(url, UriKind.Absolute);
                    //System.IO.File.Delete(uri.LocalPath);
                }
                con.Close();
            }
        }

我们正在使用的是MySql.Data.dll版本6.9.5.0。
这会导致错误:mysql执行命令期间遇到严重错误。你有任何想法为什么会失败?

请问您能提供完整的错误堆栈跟踪吗?可能与此无关,但您应该将 if (update == "subSec") 更改为 if (update != "mainSec"),以确保参数绑定与上面定义的正确查询同步。 - StuartLC
1
@StuartLC 我可以看出这可能是问题所在。如果它看到 update != 'mainSec',它将使用 subSec 查询。然而,如果 update 也不等于 'subSec',那么它就不会分配 '@subId' 值,从而导致致命错误。 - Ryan Gibbs
请按照此页面上的所有说明捕获异常详细信息,包括所有内部异常,然后使用该信息编辑您的问题:http://idownvotedbecau.se/noexceptiondetails/ - Bradley Grainger
谢谢,伙计们!我找到了问题的根本原因。 "update" 的值是 "SubSec",导致比较不正确。感谢@StuartLC和Ryan Gibbs提供的意见。 - Vishal S. Patel
1个回答

2

简述

由于分支比较不匹配,您正在执行一个带有6个未绑定变量的查询,但只绑定了5个参数。

详情

堆栈跟踪/异常中没有提供足够的信息来做出明确的答复,但看起来上面分支中的不良实践是根本原因,即在这两个分支中:

if (update == "mainSec")
{
    query = ... Query has 5 unbound variables
}
else
{
    query = ... Query has 6 unbound variables
}

并且

if (update == "subSec")
{
     ... bind the 6th parameter here
}

因为 update 类型/模式字符串没有被限制在 mainSecsubSec 的范围内,所以有一个分支使用了带有 6 个参数标记的 sub_section 查询,但未绑定第六个标记,导致出现错误。
在这种情况下,我建议您不要使用弱约束的字符串,而是严格约束您的 update 输入范围,例如使用枚举。
enum UpdateMode
{
    Invalid = 0, // This will be the default, and can be used to ensure assignment
    MainSection,
    SubSection
}

由于只有两种可能的模式,您可以通过条件赋值避免第一个查询分支,即:

Contract.Assert(updateMode != UpdateMode.Invalid);
var query = updateMode == UpdateMode.MainSection
      ? "update main_section set contentType=@contentType ... "
      : "update sub_section set subContentType=@contentType ... ";

这样做的好处在于声明和赋值可以绑定在一起(并提供了额外的编译器保证,即必须分配给query)。

(如果有两个以上的查询(和两个以上的枚举状态),那么static IReadOnlyDictionary<enum, string>将允许扩展此模式。)

绑定也会发生变化:

if (updateMode == UpdateMode.SubSection)
{
    cmd.Parameters.AddWithValue("@subId", subId);
}

一些注释

  • 不需要使用 con.Close();,因为你已经在new Connection 周围有一个using - 如果连接打开,则Dispose将调用.Close
  • 我知道这是被注释掉的,但我强烈建议此时不要进行文件IO

 if (st == 1)
 {
     // File.IO
 }

鉴于关注点分离的角度来看,删除文件应该在其他地方完成。如果删除取决于更新一行数据,则可以从此Blob更新方法返回。

  • 输入/输出操作应该在using块的范围内,这将潜在地阻止MySql连接(到连接池)的释放。
  • IO可能会失败,并且根据任何事务控制,这可能会使您的系统处于问题状态,其中记录已被删除但文件未被删除。

感谢@Stuart分享如此有价值的编程方法论。 - Vishal S. Patel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接