我在我的应用中遇到了上述错误。这是原始代码
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();
}
}
有更好的解决方法吗?
答案 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。类似的东西:
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();