无法转换类型为“System.DBNull”的对象 输入“System.String”

发布于 2024-07-19 06:49:17 字数 955 浏览 3 评论 0原文

我在我的应用程序中收到上述错误。 原始代码

public string GetCustomerNumber(Guid id)
{
     string accountNumber = 
          (string)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidmyApp, 
                          CommandType.StoredProcedure, 
                          "GetCustomerNumber", 
                          new SqlParameter("@id", id));
     return accountNumber.ToString();
 }

这是我替换为的

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));
    if (accountNumber is System.DBNull)
    {
       return string.Empty;
    }
    else
    {
       return accountNumber.ToString();
    }
}

是否有更好的方法解决这个问题?

I got the above error in my app. Here is the original code

public string GetCustomerNumber(Guid id)
{
     string accountNumber = 
          (string)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidmyApp, 
                          CommandType.StoredProcedure, 
                          "GetCustomerNumber", 
                          new SqlParameter("@id", id));
     return accountNumber.ToString();
 }

I replaced with

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));
    if (accountNumber is System.DBNull)
    {
       return string.Empty;
    }
    else
    {
       return accountNumber.ToString();
    }
}

Is there a better way around this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

高冷爸爸 2024-07-26 06:49:17

通过一个简单的通用函数,您可以使这变得非常容易。 只需这样做:

return ConvertFromDBVal<string>(accountNumber);

使用该函数:

public static T ConvertFromDBVal<T>(object obj)
{
    if (obj == null || obj == DBNull.Value)
    {
        return default(T); // returns the default value for the type
    }
    else
    {
        return (T)obj;
    }
}

With a simple generic function you can make this very easy. Just do this:

return ConvertFromDBVal<string>(accountNumber);

using the function:

public static T ConvertFromDBVal<T>(object obj)
{
    if (obj == null || obj == DBNull.Value)
    {
        return default(T); // returns the default value for the type
    }
    else
    {
        return (T)obj;
    }
}
一绘本一梦想 2024-07-26 06:49:17

可以使用更短的形式:

return (accountNumber == DBNull.Value) ? string.Empty : accountNumber.ToString()

编辑:还没有注意ExecuteScalar。 如果返回结果中不存在该字段,它确实返回 null。 所以改用:

return (accountNumber == null) ? string.Empty : accountNumber.ToString() 

A shorter form can be used:

return (accountNumber == DBNull.Value) ? string.Empty : accountNumber.ToString()

EDIT: Haven't paid attention to ExecuteScalar. It does really return null if the field is absent in the return result. So use instead:

return (accountNumber == null) ? string.Empty : accountNumber.ToString() 
独闯女儿国 2024-07-26 06:49:17

ExecuteScalar 将返回

  • 如果没有结果集,则
  • null ,否则返回结果集第一行的第一列,这可能是 DBNull。

如果您知道结果集的第一列是字符串,那么要涵盖所有基础,您需要检查 null 和 DBNull。 类似于:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null) ? String.Empty : accountNumber.ToString();

上面的代码依赖于 DBNull.ToString 返回空字符串的事实。

如果 accountNumber 是另一种类型(比如整数),那么您需要更明确:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null || Convert.IsDBNull(accountNumber) ?     
         (int) accountNumber : 0;

如果您确定结果集始终至少有一行(例如 SELECT COUNT(*)...),那么您可以跳过对 null 的检查。

在您的情况下,错误消息“无法将类型为‘System.DBNull’的对象转换为类型为‘System.String`”表示结果集的第一列是 DBNUll 值。 这是来自第一行的字符串转换:

string accountNumber = (string) ... ExecuteScalar(...);

Marc_s 的评论说你不需要检查 DBNull.Value 是错误的。

ExecuteScalar will return

  • null if there is no result set
  • otherwise the first column of the first row of the resultset, which may be DBNull.

If you know that the first column of the resultset is a string, then to cover all bases you need to check for both null and DBNull. Something like:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null) ? String.Empty : accountNumber.ToString();

The above code relies on the fact that DBNull.ToString returns an empty string.

If accountNumber was another type (say integer), then you'd need to be more explicit:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null || Convert.IsDBNull(accountNumber) ?     
         (int) accountNumber : 0;

If you know for sure that your resultset will always have at least one row (e.g. SELECT COUNT(*)...), then you can skip the check for null.

In your case the error message "Unable to cast object of type ‘System.DBNull’ to type ‘System.String`" indicates that the first column of your result set is a DBNUll value. This is from the cast to string on the first line:

string accountNumber = (string) ... ExecuteScalar(...);

Marc_s's comment that you don't need to check for DBNull.Value is wrong.

我不会写诗 2024-07-26 06:49:17

您可以使用 C# 的空合并运算符

return accountNumber ?? string.Empty;

You can use C#'s null coalescing operator

return accountNumber ?? string.Empty;
放低过去 2024-07-26 06:49:17

这是我用来转换任何可能是 DBNull.Value 的对象的通用方法:

public static T ConvertDBNull<T>(object value, Func<object, T> conversionFunction)
{
    return conversionFunction(value == DBNull.Value ? null : value);
}

用法:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

更短:

return command
    .ExecuteScalar()
    .ConvertDBNull(Convert.ToInt32);

This is the generic method that I use to convert any object that might be a DBNull.Value:

public static T ConvertDBNull<T>(object value, Func<object, T> conversionFunction)
{
    return conversionFunction(value == DBNull.Value ? null : value);
}

usage:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

shorter:

return command
    .ExecuteScalar()
    .ConvertDBNull(Convert.ToInt32);
双手揣兜 2024-07-26 06:49:17

还有另一种方法可以解决此问题。 修改一下你的商店流程怎么样? 通过使用 ISNULL(your field, "") sql 函数,如果返回值为 null,则可以返回空字符串。

然后你就得到了原始版本的干净代码。

There is another way to workaround this issue. How about modify your store procedure? by using ISNULL(your field, "") sql function , you can return empty string if the return value is null.

Then you have your clean code as original version.

善良天后 2024-07-26 06:49:17

我想你可以这样做:

string accountNumber = DBSqlHelperFactory.ExecuteScalar(...) as string;

如果 accountNumber 为空,则意味着它是 DBNull 而不是字符串:)

I suppose you can do it like this:

string accountNumber = DBSqlHelperFactory.ExecuteScalar(...) as string;

If accountNumber is null it means it was DBNull not string :)

故事未完 2024-07-26 06:49:17

String.Concat 将 DBNull 和 null 值转换为空字符串。

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));

    return String.Concat(accountNumber);

 }

然而,我认为你在代码的可理解性上失去了一些东西

String.Concat transforms DBNull and null values to an empty string.

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));

    return String.Concat(accountNumber);

 }

However, I think you lose something on code understandability

蓝眼睛不忧郁 2024-07-26 06:49:17

由于我得到了一个不为 null 的实例,并且如果与 DBNULL 进行比较,我得到了 Operator '==' can not apply to operands of type 'string' and 'system.dbnull' 例外,
如果我尝试更改为与 NULL 进行比较,它根本不起作用(因为 DBNull 是一个对象),即使这是公认的答案。

我决定简单地使用“is”关键字。
所以结果非常可读:

data = (item is DBNull) ? String.Empty:项目

Since I got an instance which isn't null and if I compared to DBNULL I got Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull' exeption,
and if I tried to change to compare to NULL, it simply didn't work ( since DBNull is an object) even that's the accepted answer.

I decided to simply use the 'is' keyword.
So the result is very readable:

data = (item is DBNull) ? String.Empty : item

故人的歌 2024-07-26 06:49:17

使用更新的 C# 语法并考虑可空类型的更简洁的方法:

private static T? FromDbNull<T>(object? obj) => 
    obj == null || obj == DBNull.Value ? default : (T)obj;

可以与数据读取器一起使用,如下所示:

        while (reader.Read())
        {
            var newObject = new SomeObject(
                FromDbNull<string?>(reader["nullable_field_1"]),
                FromDbNull<string?>(reader["nullable_field_2"]),
                FromDbNull<string?>(reader["nullable_field_3"]), 
                FromDbNull<double?>(reader["nullable_field_4"])
            );
            
            response.Add(newObject);
        }

A more concise approach using more recent C# syntax and also accounting for nullable types:

private static T? FromDbNull<T>(object? obj) => 
    obj == null || obj == DBNull.Value ? default : (T)obj;

Can be used with a data reader as follows:

        while (reader.Read())
        {
            var newObject = new SomeObject(
                FromDbNull<string?>(reader["nullable_field_1"]),
                FromDbNull<string?>(reader["nullable_field_2"]),
                FromDbNull<string?>(reader["nullable_field_3"]), 
                FromDbNull<double?>(reader["nullable_field_4"])
            );
            
            response.Add(newObject);
        }
倦话 2024-07-26 06:49:17

因为我自己也遇到了类似的错误,这是谷歌上的第一个点击。
如果您没有将可空属性配置为与您的数据库匹配,则可能会发生相同的错误。

如果您只需要快速& 丑陋的修复,你可以添加:

#nullable 禁用

在模型类的开头禁用。

Since I just had a similar error myself and this is the first hit on google.
If you do not have your properties nullable configured to match your database, the same error can occur.

If you just need a quick & ugly fix, you can add:

#nullable disable

at the start of your model class.

三月梨花 2024-07-26 06:49:17

基于 @rein

public static class DbDataReaderExtensions
{
    public static TObjProp Get<TObj, TObjProp>(
        this DbDataReader reader,
        Expression<Func<TObj, TObjProp>> expression)
    {
        MemberExpression member = expression.Body as MemberExpression;
        string propertyName = member.Member.Name;

        //PropertyInfo propInfo = member.Member as PropertyInfo;

        var recordOrdinal = reader.GetOrdinal(propertyName);
        var obj = reader.GetValue(recordOrdinal);

        if (obj == null || obj == DBNull.Value)
        {
            return default(TObjProp);
        }
        else
        {
            return (TObjProp)obj;
        }
    }
}

给出的答案:

public class MyClass
{
    public bool? IsCheckPassed { get; set; }
}

使用 as:

var test = reader.Get<MyClass, bool?>(o => o.IsCheckPassed);

或者,如果您在异常方法中硬编码类类型:

var test = reader.Get(o => o.IsCheckPassed);

ps 我还没有弄清楚如何在不牺牲代码长度的情况下使 generics 隐式。免费发表评论并提出改进建议

完整示例:

public async Task<MyClass> Test(string connectionString) {
    var result = new MyClass();
    
    await using var con = new SQLiteConnection(connectionString);
    con.Open();

    await using var cmd = con.CreateCommand();
    cmd.CommandText = @$"SELECT Id, IsCheckPassed FROM mytable";
    
    var reader = await cmd.ExecuteReaderAsync();
    while (reader.Read()) {
        // old, not working! Throws exception!
        //bool? isCheckPassed1 = reader.GetBoolean(reader.GetOrdinal("IsCheckPassed"));
        
        // old, working, but too long (also if you have like 20 properties then all the more reasons to refactor..)
        bool? isCheckPassed2 = null;
        bool? isCheckPassed2Temp = reader.GetValue(reader.GetOrdinal("IsCheckPassed"));
        if (isCheckPassed2Temp != null && isCheckPassed2Temp != DBNull.Value)
            isCheckPassed2 = (bool?)isCheckPassed2Temp;
        
        // new
        var isCheckPassed3 = reader.Get<MyClass, bool?>(o => o.IsCheckPassed);
        // repeat for 20 more properties :)
        
        result.IsCheckPassed = isCheckPassed3;
    }
    
    return result;
}

只要表列名称与类的属性名称匹配,解决方案就有效。 并且可能不是生产级性能明智的,因此使用或修改需要您自担风险:)

based on answer from @rein

public static class DbDataReaderExtensions
{
    public static TObjProp Get<TObj, TObjProp>(
        this DbDataReader reader,
        Expression<Func<TObj, TObjProp>> expression)
    {
        MemberExpression member = expression.Body as MemberExpression;
        string propertyName = member.Member.Name;

        //PropertyInfo propInfo = member.Member as PropertyInfo;

        var recordOrdinal = reader.GetOrdinal(propertyName);
        var obj = reader.GetValue(recordOrdinal);

        if (obj == null || obj == DBNull.Value)
        {
            return default(TObjProp);
        }
        else
        {
            return (TObjProp)obj;
        }
    }
}

Given:

public class MyClass
{
    public bool? IsCheckPassed { get; set; }
}

Use as:

var test = reader.Get<MyClass, bool?>(o => o.IsCheckPassed);

or, if you hardcode class type in exception method:

var test = reader.Get(o => o.IsCheckPassed);

p.s. I haven't figured yet how to make generics implicit without sacrificing code length.. fee free to comment and suggest improvements

Full example:

public async Task<MyClass> Test(string connectionString) {
    var result = new MyClass();
    
    await using var con = new SQLiteConnection(connectionString);
    con.Open();

    await using var cmd = con.CreateCommand();
    cmd.CommandText = @
quot;SELECT Id, IsCheckPassed FROM mytable";
    
    var reader = await cmd.ExecuteReaderAsync();
    while (reader.Read()) {
        // old, not working! Throws exception!
        //bool? isCheckPassed1 = reader.GetBoolean(reader.GetOrdinal("IsCheckPassed"));
        
        // old, working, but too long (also if you have like 20 properties then all the more reasons to refactor..)
        bool? isCheckPassed2 = null;
        bool? isCheckPassed2Temp = reader.GetValue(reader.GetOrdinal("IsCheckPassed"));
        if (isCheckPassed2Temp != null && isCheckPassed2Temp != DBNull.Value)
            isCheckPassed2 = (bool?)isCheckPassed2Temp;
        
        // new
        var isCheckPassed3 = reader.Get<MyClass, bool?>(o => o.IsCheckPassed);
        // repeat for 20 more properties :)
        
        result.IsCheckPassed = isCheckPassed3;
    }
    
    return result;
}

Solution will work for as long as table column names match property names of the class. And might not be production-grade performance wise, so use or modify at your own risk :)

悲喜皆因你 2024-07-26 06:49:17

将其转换为

string s = System.DBNull.value.ToString();

Convert it Like

string s = System.DBNull.value.ToString();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文