无法将类型为“System.DBNull”的对象强制转换为“System.String”

时间:2009-05-15 20:22:09

标签: c# asp.net database null

我在我的应用中遇到了上述错误。这是原始代码

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();
    }
}

有更好的解决方法吗?

12 个答案:

答案 0 :(得分:179)

使用简单的通用功能,您可以轻松完成此操作。就这样做:

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;
    }
}

答案 1 :(得分:76)

可以使用更短的表格:

return (accountNumber == DBNull.Value) ? string.Empty : accountNumber.ToString()
编辑:没有注意到ExecuteScalar。如果返回结果中没有该字段,它确实返回null。所以请改用:

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

答案 2 :(得分:15)

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是错误的。

答案 3 :(得分:6)

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

return accountNumber ?? string.Empty;

答案 4 :(得分:3)

还有另一种方法可以解决此问题。如何修改您的商店程序?通过使用ISNULL(您的字段,“”)sql函数,如果返回值为null,则可以返回空字符串。

然后你将干净的代码作为原始版本。

答案 5 :(得分:3)

这是我用来转换任何可能是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);

答案 6 :(得分:2)

我想你可以这样做:

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

如果accountNumber为null,则表示它是DBNull而不是字符串:)

答案 7 :(得分:1)

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);

 }

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

答案 8 :(得分:0)

由于我得到了一个不为null的实例,并且如果与DBNULL比较,我得到了<div class="row"> <div class="col-md-3"> <label for="sel1">Evauación Criticidad</label> </div> <div class="col-md-9"> <select class="form-inline" > <option>1</option> <option>2</option> <option>3</option> <option>4</option> <option>5</option> </select> </div> </div> 的证明, 并且如果我尝试更改以与NULL进行比较,即使被接受了,它也根本无法工作(因为DBNull是一个对象)。

我决定只使用'is'关键字。  因此结果非常可读:

Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull'

答案 9 :(得分:0)

基于@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; }
}

用作:

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;
}

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

答案 10 :(得分:-1)

我使用扩展程序为我消除了这个问题,这可能是也可能不是你想要的。

它是这样的:

public static class Extensions
{

    public String TrimString(this object item)
    {
        return String.Format("{0}", item).Trim();
    }

}

注意:

此扩展程序返回null值!如果该项为null DBNull.Value ,则会返回一个空字符串。

用法:

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

答案 11 :(得分:-2)

将其转换为

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