检查数据库记录映射中的空值

如何检查附加代码中的db null值? 请理解我是一个新的C#转换…

此代码的作用是获取IDataReader对象并将其转换并映射到强类型的对象列表。 但我发现,当读取器中返回空列时,它完全出错。

变流器

internal class Converter where T : new() { // Declare our _converter delegate readonly Func _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 GetMapFunc() { // declare our field count int _fc = dataReader.FieldCount; // declare our expression list List exps = new List(); // 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>(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 Map(DbDataReader dr) where T : new() { try { // initialize our returnable list List list = new List(); // fire up the lamda mapping var converter = new Converter(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); } } 

我只是不太清楚键入对象的列在这里发生了什么,所以我会尝试自己做…但我只是不知道它在哪里。

我知道这可能没多大帮助,但我得到的错误是:

Unable to cast object of type 'System.DBNull' to type 'System.String'.

它发生在

 internal T CreateItemFromRow() { return _converter(dataReader); //<-- Here } 

注意

如果我使用ISNULL(列,”)将查询本身中的列包装起来,则不会发生这种情况,但我相信您可以理解这肯定不是解决方案

问题在于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; 

这是一般处理数据集时最烦人的问题之一。

我通常会解决它的方法是将DBNull值转换为更有用的值,例如在某些情况下实际为null或甚至是空字符串。 这可以通过多种方式完成,但最近我采用了扩展方法。

 public static T? GetValueOrNull(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(); 

或者更通用的只是将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放入字符串中。

希望这可能对你有所帮助。