【问题标题】:How to handle SQL errors that are not handled by the try/catch block and result in System.TypeInitializationException?如何处理 try/catch 块未处理并导致 System.TypeInitializationException 的 SQL 错误?
【发布时间】:2021-08-20 13:47:55
【问题描述】:

这是一个 Windows 窗体应用程序。下面的代码是一个测试数据库连接性的静态类。在页面加载方法中调用Test1.test

public static class Test1
    {
       static string cStr = "Data Source=svrname;Initial Catalog=dbname;Integrated Security=true";
       public static bool test{ get; } = chkDb();


        private static bool chkDb()
        {
            try
            {
                using (SqlConnection connection = new SqlConnection(cStr ))
                {
                    SqlCommand command = new SqlCommand("SELECT 1;", connection);
                    connection.Open();
                    return true;
                }
            }
            catch {
                return false;
            }
        }

此代码通常适用于文件。但是,如果我输入了一个无效的 dbname,那么它会给出以下未处理的异常,这似乎绕过了 catch 块。当无效的 Windows 用户尝试打开应用程序时,也会发生类似的情况。

System.TypeInitializationException: '类型初始化器 'Project1.Test1' 引发了异常。'

内部异常: SqlException:无法打开登录请求的数据库“dbname”。 登录失败。用户“DOMAINXYZ\User1”登录失败。

我该如何处理这个异常?我尝试过捕获异常、SqlException,但似乎都没有。

【问题讨论】:

  • 将其作为静态属性(或根本没有)似乎是个坏主意。数据库的状态可以随时更改,并且不受您的代码的控制。它回答true 并没有告诉您数据库仍然在您尝试使用它时仍然可用。它回答 false 告诉你它在 5 毫秒前不可用,但没有关于它现在是否可用。
  • 非平凡的静态初始化器是邪恶的,像瘟疫一样避免它们。这是您想要这样做的一个例子。这里没有充分的理由使用静态初始化程序;只需使用常规方法使其成为常规实例并在启动代码中调用它。这不能回答为什么这段代码会失败的问题(我认为它实际上不应该,所以这 可能 是 JIT 中的一个错误,或者它可能是静态初始化的一些微妙细节,其中这实际上是预期的结果),但最好避免一开始就问这些问题。
  • 更喜欢实例而不是静态;如果您可以集中访问(例如,它仅以一种形式或一个数据访问层类使用)使其成为那里的实例字段并在(非静态!)构造函数中初始化事物,这是一个仅调用一次的单独方法,或者使用Lazy。您可以将实例传递给那些需要它们的方法,并且只能传递给那些(依赖注入)。如果您必须将其设为静态,请坚持使用Lazy;这使得检索发生的时间可以预测,并且可以在调用代码中捕获异常。
  • 我无法重现 TypeInitializationException。您使用的是哪个框架版本?或者你是否过度简化了你的类,使其不再重现错误?
  • @DavidBrowne-Microsoft 如果我错了,请纠正我,但恕我直言,自动属性将有一个 private static readonly <somebackingfield> = chkDb();。所以这可能会在 chkDb 中不使用 try/catch 就抛出 TypeInitializationException。

标签: c# sql-server


【解决方案1】:

完整的复制:

using System;
using System.Data.SqlClient;
using System.Windows.Forms;

namespace WindowsFormsApp5
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            bool b = Test1.test;
            textBox1.Text = b.ToString();
            textBox2.Text = Test1.error;
        }
    }

    public static class Test1
    {
        static string cStr = "Data Source=svrname;Initial Catalog=dbname;Integrated Security=true";
        public static bool test { get; } = chkDb();
        public static string error;

        private static bool chkDb()
        {
            try
            {
                using (SqlConnection connection = new SqlConnection(cStr))
                {
                    SqlCommand command = new SqlCommand("SELECT 1;", connection);
                    connection.Open();
                    return true;
                }
            }
            catch (Exception ex)
            {
                error = ex.Message;
                return false;
            }
        }
    }
}

输出:

这是在 Windows 10 上使用 .NET Framework 4.8 测试的

如果我将 servername 更改为可访问的服务器,结果是:

而且,如果我还将Initial Catalog 更改为现有的,第一个文本框将获得值True(第二个文本框保持为空)。

编辑: 当我将 3 行从 Form1 移动到 Form1_Load 时:

       public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bool b = Test1.test;
            textBox1.Text = b.ToString();
            textBox2.Text = Test1.error;
        }

结果不变。

【讨论】:

  • 您可以尝试在表单加载事件中运行bool b = Test1.test; 吗?我现在无法访问我的计算机,但稍后会比较代码。
  • 我怀疑这有什么不同,但请把测试代码移到 form_load 中。加载表单后,您可能会有另一个同步上下文。我没有可玩的 SQL 服务器...
  • 添加了(请求的)更改。
  • @variable 轮到你了 ;) 谢谢,Luuk!
【解决方案2】:

这只会在类加载时检查一次数据库连接。要将检查移出类型初始值设定项,可以这样写:

    public static bool test 
    { 
        get
        {
            return chkDb();
        }
    } 

或者让它成为一个方法而不是一个属性。

catch 块将捕获异常。您可能只是在调试并看到第一次机会异常。

【讨论】:

  • 我已将其作为解决此问题的方法。第一次看到catch无法捕获异常的问题。
  • 好。它确实应该是一种方法。
  • 第一次机会例外是什么意思?
  • 好的,你的意思是说这是一个调试器设置,在生产中这个问题永远不会出现?另外,您能告诉我不捕获上述异常是否是默认调试器设置?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-03
  • 1970-01-01
  • 1970-01-01
  • 2010-09-12
相关资源
最近更新 更多