【问题标题】:Handling null values in Command.ExecuteScalar when the value is converted to a Type将值转换为类型时在 Command.ExecuteScalar 中处理空值
【发布时间】:2012-10-04 14:52:08
【问题描述】:

我有一个函数可以执行查询并返回一个标量值,如下所示:

        public T GetScalarValue<T>(string sql)
        {
            T t;
            try {
                SqlConnection conn = new SqlConnection("MY_SQL_SERVER_CONNECTION_STRING");
                conn.Open();
                SqlCommand cmd = new SqlCommand(sql, conn);
                t = (T)cmd.ExecuteScalar();
            }
            catch (Exception ex) {
                throw ex;
            }
            finally{
                if(conn.State == ConnectionState.Open)
                    conn.Close();
            }
            return t;
        }

当我用给定的 SQL 调用这个函数时,我会得到结果:

int result = dal.GetScalarValue<int>("SELECT [schema_id] FROM SYS.SCHEMAS WHERE [name] = 'dbo'")

由于查询返回一个 int 值,这可以正常工作,但是当我为不返回任何内容的查询调用相同的值时,我的函数将失败:

int result = dal.GetScalarValue<int>("SELECT [schema_id] FROM SYS.SCHEMAS WHERE [name] = 'Foooo'")

由于 cmd.ExecuteScalar() 没有返回结果,它会给出“对象引用未设置为对象的实例”异常。但我希望这个函数返回给定类型的默认值(例如:对于 int,它应该返回 0,十进制 - 0.00,对于字符串 - 一个空值,对于 datetime - '1900-01-01',等等)。

如果您找到更好的解决方案,请告诉我。

提前感谢您的帮助。

【问题讨论】:

    标签: c# c#-4.0 .net-4.0 ado.net


    【解决方案1】:

    稍微重构您的代码以提高可读性:

    public T GetScalarValue<T>(string sql) where T : struct
    {
        string connectionString = "MY_SQL_SERVER_CONNECTION_STRING";
    
        using (var connection = new SqlConnection(connectionString))
        using (var command = new SqlCommand(sql, connection))
        {
            connection.Open();
            var obj = command.ExecuteScalar();
    
            return (obj != null) ? (T)obj : default(T);
        }
    }
    

    【讨论】:

    • return 语句中的条件是错误的,我个人看不到您的where T : struct 要求的使用。无论如何+1,你的答案的核心看起来不错:)
    【解决方案2】:

    试试这样的:

    public T GetScalarValue<T>(string sql)
    {
        T result = default(T);
    
        try
        {
            using (SqlConnection conn = new SqlConnection("MY_SQL_SERVER_CONNECTION_STRING"))
            using (SqlCommand cmd = new SqlCommand(sql, conn))
            {
                conn.Open();
    
                var scalar = cmd.ExecuteScalar();
    
                if (scalar != null)
                {
                    result = (T)Convert.ChangeType(scalar, typeof(T));
    
                    // or result = (T)scalar    if that works better for you.
                }
            }
    
            return result;
        }
        catch (Exception ex)
        {
            // Log the exception maybe?
            return result;
        }
    }
    

    这里我将result 设置为T 类型的默认值。接下来,如果有异常,那么我就在此处返回result。因此,在您失败的示例中,将返回值 0。

    编辑:

    在 cmets 中提出了一个很好的观点。在我给你的异常处理程序中,我只是返回result。最好记录或以某种方式记录异常,以便您知道是否出了问题。一个想法是使用诸如 NLogLog4Net 之类的记录器来记录异常 - 有很多选择。

    【讨论】:

    • 谢谢@Jason Evans。但这仍然会失败。我应该做这样的事情吗?对象 obj = cmd.ExecuteScalar(); if (obj != null) t = (T)obj;
    • 谢谢@Jason,让我看看是否有人有更好的解决方案!
    • 我已经更改了转换结果的方式,因为我记得使用 Convert.ChangeType 比通过 (T) 进行投射更彻底。
    • @JasonEvans Convert.ChangeType 将允许将int 转换为double,但不允许将int 转换为int?。我认为您的早期版本更好,后者的转换对 IMO 更有用。也就是说,盲目地忽略任何和所有异常是非常糟糕的,并且使调用者无法确定 default(T) 是否是实际返回的内容,或者(例如)数据库连接已丢失。
    猜你喜欢
    • 2017-05-04
    • 2019-05-22
    • 1970-01-01
    • 2022-12-19
    • 1970-01-01
    • 2019-07-08
    • 2022-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多