用于从可能的null db值分配值的优雅语法

时间:2009-11-10 20:05:02

标签: c#

对于某些上下文,记录类SaleAmount属性为

public decimal? SaleAmount
{
    get;
    set;
}

理想情况下,我会选择

record.SaleAmount.Value = 
sql.Reader.IsDBNull(IDX_SALEAMOUNT) ? null : 
    sql.Reader.GetDecimal(IDX_SALEAMOUNT);

唉,编译器,这不是朋友,因为......

无法确定条件表达式的类型,因为''和'十进制'之间没有隐式转换

那么你如何优雅地表达这一点,并且不要像下面那样玩明显的卡片......

if (!sql.Reader.IsDBNull(IDX_SALEAMOUNT))
    record.SaleAmount = sql.Reader.GetDecimal(IDX_SALEAMOUNT);

或者高于最佳解决方案?

10 个答案:

答案 0 :(得分:12)

如果您正在使用C#3,我实际上使用扩展方法可以让您更轻松:

public static decimal? GetNullableDecimal(this DbReader reader, 
    int column)
{
    return reader.IsDBNull(column) ? (decimal?) null : 
        reader.GetDecimal(column);
}

然后您的应用程序代码可以使用:

record.SaleAmount = sql.Reader.GetNullableDecimal(IDX_SALEAMOUNT);

答案 1 :(得分:3)

你可以选择:

record.SaleAmount = 
    (sql.Reader.IsDBNull(IDX_SALEAMOUNT) ? null : 
     sql.Reader.GetDecimal(IDX_SALEAMOUNT)) as decimal?;

你可以尝试另一种方式:

// usage: new GenericDataReader(cm.ExecuteReader())
//        ...Get<decimal?>(...);
public class GenericDataReader : IDataReader
{
    // IDataReader implementation
    public T Get<T>(int ordinal)
    {
        if (_dataReader.IsDBNull(ordinal))
            return default(T);
        else
            return (T)_dataReader.GetValue(ordinal);
    }
}

有关此方法的更多信息here

答案 2 :(得分:3)

我喜欢使用以下通用扩展方法(可以很容易地转换为静态辅助函数)来从读取器获取可能的空值:

    public static T Get<T>(this IDataReader reader, string columnName)
    {
        if (reader[columnName] == DbNull.Value)
        {
            return default(T);
        }

        return (T)reader[columnName];
    }

答案 3 :(得分:2)

您收到该错误,因为类型decimal(值类型)和null(参考值)不能一起播放。

此外,由于您要分配Value类型的Nullable<decimal>媒体资源,因此您实际上是在尝试将null分配给decimal类型,是不行的。

您可以通过投射来明确地将GetDecimal结果设为可为decimal类型:

record.SaleAmount = sql.Reader.IsDBNull(IDX_SALEAMOUNT) ? 
    null : (decimal?)sql.Reader.GetDecimal(IDX_SALEAMOUNT);

我会采用你的第二种方法。

答案 4 :(得分:1)

首先,Value属性是只读的,所以无论如何你都无法分配它。

如果将一个强制转换添加到条件的false部分以转换为可以为null的十进制,编译器应该很高兴。请尝试以下

record.SaleAmount = sql.Reader.IsDBNull(IDX_SALEAMOUNT) ? 
    null : (decimal?) sql.Reader.GetDecimal(IDX_SALEAMOUNT);

答案 5 :(得分:0)

为什么不使用空合并运算符?

record.SaleAmount = (decimal?)sql.Reader.GetDecimal(IDX_SALEAMOUNT)
    ?? null;

答案 6 :(得分:0)

对我来说,最干净的解决方案是将它从业务逻辑层中完全删除,然后让查询首先向读者返回一个非空值

例如,如果使用SQL Server ..

SELECT IsNull(SalesAmount,0) ...

答案 7 :(得分:0)

我使用具有Nullable&lt; T&gt;的FieldHelper类。 ToXXX(对象)方法。这是 十进制的例子(这是在Linq之前的日子里写的 - 大约2 - 3年前, 欢迎您将委托构造替换为Linq):

/// <summary>
/// Gets a nullable value.
/// </summary>
/// <param name="aValue">The value to be converted.</param>
/// <returns>The converted value.</returns>
public static Nullable<decimal> ToDecimal(object aValue)
{
   return ToNullable<decimal>(aValue,
     delegate(object aConvertableValue)
     {
        return Convert.ToDecimal(aConvertableValue);
     });
}

/// <summary>
/// Converts the given value if necessary.
/// </summary>
/// <param name="aValue">A value from the database.</param>
/// <param name="aConversion">A conversion delegate.</param>
/// <returns>null, or the given value.</returns>
private static Nullable<T> ToNullable<T>(
        object aValue, Converter<T> aConverter) where T : struct
{
  if (aValue == DBNull.Value || aValue == null)
     return null;
  else if (aValue is T)
     return (T)aValue;
  else
     return aConverter(aValue);
}

答案 8 :(得分:0)

为了解决这个问题,不要在运行时开销方面花费太多,你恐怕不能太挑剔。

但是,语法更简洁:

public static Nullable<T> GetNullableValue<T>(this IDataRecord record, 
    int columnIndex, Func<int, T> getValue)
  where T: struct;
{
  if (record.IsDbNull(columnIndex))
    return null;
  else
    return getValue(columnIndex);
}

var xyz = reader.GetNullableValue(0, reader.GetDecimal);

答案 9 :(得分:0)

IsDbNull(int)通常比使用GetSqlInt32等方法慢,然后比较DBNull.Value或使用它自己的.IsNull喜欢:

    public static int Int32(this SqlDataReader r, int ord)
    {
        var t = r.GetSqlInt32(ord);
        return t.IsNull ? default(int) : t.Value;
    }

尝试了一些模板解决方案但到目前为止无济于事。问题是所有Sql类型(此处为SqlInt32)类型实际上是结构,而它们都有.Value属性C#没有真正的模板来处理它。它们也有自己的INullable接口,它只有.IsNull,并且与Nyllable&lt;&gt;不相容。

我怀疑人们需要完整的Sql类型作为C#模板,或者将ICOnvertible添加到它们中,以便能够只有一个或两个模板化方法。

如果有人可能有一个功能性技巧的想法或两个说出来:-)