如何检查附加代码中的db null值?请理解我是一个新的C#转换...
此代码的作用是获取IDataReader对象并将其转换并映射到强类型的对象列表。但我发现,当读者返回空列时,它完全出错。
internal class Converter<T> where T : new()
{
// Declare our _converter delegate
readonly Func<IDataReader, T> _converter;
// Declare our internal dataReader
readonly IDataReader dataReader;
// Build our mapping based on the properties in the class/type we've passed in to the class
private Func<IDataReader, T> GetMapFunc()
{
// declare our field count
int _fc = dataReader.FieldCount;
// declare our expression list
List<Expression> exps = new List<Expression>();
// build our parameters for the expression tree
ParameterExpression paramExp = Expression.Parameter(typeof(IDataRecord), "o7thDR");
ParameterExpression targetExp = Expression.Variable(typeof(T));
// Add our expression tree assignment to the exp list
exps.Add(Expression.Assign(targetExp, Expression.New(targetExp.Type)));
//does int based lookup
PropertyInfo indexerInfo = typeof(IDataRecord).GetProperty("Item", new[] { typeof(int) });
// grab a collection of column names from our data reader
var columnNames = Enumerable.Range(0, _fc).Select(i => new { i, name = dataReader.GetName(i) }).AsParallel();
// loop through all our columns and map them properly
foreach (var column in columnNames)
{
// grab our column property
PropertyInfo property = targetExp.Type.GetProperty(column.name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
// check if it's null or not
if (property != null)
{
// build our expression tree to map the column to the T
ConstantExpression columnNameExp = Expression.Constant(column.i);
IndexExpression propertyExp = Expression.MakeIndex(paramExp, indexerInfo, new[] { columnNameExp });
UnaryExpression convertExp = Expression.Convert(propertyExp, property.PropertyType);
BinaryExpression bindExp = Expression.Assign(Expression.Property(targetExp, property), convertExp);
// add it to our expression list
exps.Add(bindExp);
}
}
// add the originating map to our expression list
exps.Add(targetExp);
// return a compiled cached map
return Expression.Lambda<Func<IDataReader, T>>(Expression.Block(new[] { targetExp }, exps), paramExp).Compile();
}
// initialize
internal Converter(IDataReader dataReader)
{
// initialize the internal datareader
this.dataReader = dataReader;
// build our map
_converter = GetMapFunc();
}
// create and map each column to it's respective object
internal T CreateItemFromRow()
{
return _converter(dataReader);
}
}
private static IList<T> Map<T>(DbDataReader dr) where T : new()
{
try
{
// initialize our returnable list
List<T> list = new List<T>();
// fire up the lamda mapping
var converter = new Converter<T>(dr);
while (dr.Read())
{
// read in each row, and properly map it to our T object
var obj = converter.CreateItemFromRow();
// add it to our list
list.Add(obj);
}
// reutrn it
return list;
}
catch (Exception ex)
{
// make sure this method returns a default List
return default(List<T>);
}
}
我只是不太清楚键入对象的列在这里发生了什么,所以我会尝试自己做...但我只是不知道它在哪里。
我知道这可能不会有多大帮助,但我得到的错误是:
Unable to cast object of type 'System.DBNull' to type 'System.String'.
它发生在
上internal T CreateItemFromRow()
{
return _converter(dataReader); //<-- Here
}
如果我使用ISNULL(列,'')将查询本身中的列包装起来,则不会发生这种情况,但我相信您可以理解这肯定不是解决方案
答案 0 :(得分:2)
这是处理数据集时最烦人的问题之一。
我通常使用它的方法是将DBNull值转换为更有用的值,例如在某些情况下实际为null或甚至是空字符串。这可以通过多种方式完成,但最近我采用了扩展方法。
public static T? GetValueOrNull<T>(this object value) where T : struct
{
return value == null || value == DBNull.Value ? (T?) null : (T) Convert.ChangeType(value, typeof (T));
}
可空类型的便捷扩展方法,例如:
int? myInt = DataSet.Tables[0].Rows[0]["DBNullInt"].GetValueOrNull<int>();
或者只是将DBNull转换为null的更通用的:
public static object GetValueOrNull(this object value)
{
return value == DBNull.Value ? null : value;
}
string myString DataSet.Tables[0].Rows[0]["DBNullString"].GetValueOrNull();
然后您将获得一个空字符串,而不是尝试将DBNull
放入字符串中。
希望这对你有所帮助。
答案 1 :(得分:2)
问题在于convertExp = Expression.Convert(propertyExp, property.PropertyType)
行。您不能指望将DbNull
值转换为框架类型中的等效值。当您的类型是值类型时,这尤其令人讨厌。一种选择是检查db中的读取值是否为DbNull.Value
,如果是,则需要自己查找兼容值。在一些案例中,人们可以使用C#中的这些类型的默认值。如果你必须这样做
property = value == DBNull.Value ? default(T): value;
通用实现看起来像(转换器类中的foreach
):
foreach (var column in columns)
{
var property = targetExp.Type.GetProperty(
column.name,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (property == null)
continue;
var columnIndexExp = Expression.Constant(column.i);
var propertyExp = Expression.MakeIndex(
paramExp, indexerInfo, new[] { columnIndexExp });
var convertExp = Expression.Condition(
Expression.Equal(
propertyExp,
Expression.Constant(DBNull.Value)),
Expression.Default(property.PropertyType),
Expression.Convert(propertyExp, property.PropertyType));
var bindExp = Expression.Assign(
Expression.Property(targetExp, property), convertExp);
exps.Add(bindExp);
}
现在这相当于
property = reader[index] == DBNull.Value ? default(T): reader[index];
通过将读取器分配给变量并在条件检查中使用其值,可以避免读取器的双重查找。所以这应该稍微好一些,但是更复杂:
foreach (var column in columns)
{
var property = targetExp.Type.GetProperty(
column.name,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (property == null)
continue;
var columnIndexExp = Expression.Constant(column.i);
var cellExp = Expression.MakeIndex(
paramExp, indexerInfo, new[] { columnIndexExp });
var cellValueExp = Expression.Variable(typeof(object), "o7thPropValue");
var convertExp = Expression.Condition(
Expression.Equal(
cellValueExp,
Expression.Constant(DBNull.Value)),
Expression.Default(property.PropertyType),
Expression.Convert(cellValueExp, property.PropertyType));
var cellValueReadExp = Expression.Block(new[] { cellValueExp },
Expression.Assign(cellValueExp, cellExp), convertExp);
var bindExp = Expression.Assign(
Expression.Property(targetExp, property), cellValueReadExp);
exps.Add(bindExp);
}
这是以这种方式进行条件检查:
value = reader[index];
property = value == DBNull.Value ? default(T): value;
答案 2 :(得分:0)
最近我遇到这个问题 两者
Expression.TypeIs(propertyExp,typeof(DBNull));
和
Expression.Equal(propertyExp,Expression.Constant(DBNull.Value));
对我没有用,因为它们确实增加了内存分配(在这种情况下,这是我的主要担忧)
这是这两种映射器方法与Dapper在10K行查询中相比的基准。
因此,要解决此问题,结果是IDataRecord能够调用“ IsDBNull”来检查当前阅读器中的列是否为DBNull
并可以像
这样的表达式编写var isReaderDbNull = Expression.Call(paramExp, "IsDBNull", null, readerIndex);
最后,我得到了这个解决方案
现在性能再次可以接受。