在分配 IDataRecord 值时,需要一种简洁的方法来评估 NULL

本文关键字:简洁 一种 方法 NULL 评估 值时 IDataRecord 分配 | 更新日期: 2023-09-27 18:35:43

我有一个 FillDataRecord 方法,该方法将值分配给来自 IDataRecord 的对象。 它一直工作,直到遇到具有 NULL 值的字段,此时它会中断消息"数据为 Null。不能对 Null 值调用此方法或属性。

解决方法是使用IDataRecord.IsDBNull,我已经这样做了,但我想让它更干净。 这是带有一些相关注释的代码。

private static Employee FillDataRecord(IDataRecord dataRecord)
{
    Employee employee = new BusinessEntities.Employee();
    employee.EmployeeID = dataRecord.GetInt32(dataRecord.GetOrdinal("EmployeeID"));
    // Other fields omitted for brevity...
    // This breaks when StreetLine2 is NULL.
    employee.StreetLine2 = dataRecord.GetString(dataRecord.GetOrdinal("StreetLine2"));
    // This is my first workaround, which fixes the above error but is verbose.
    if (dataRecord.IsDBNull(dataRecord.GetOrdinal("StreetLine2")))
        employee.StreetLine2 = "";
    else
        employee.StreetLine2 = dataRecord.GetString(dataRecord.GetOrdinal("StreetLine2"));
    // This is my second workaround, which uses a custom method shown below.
    // But it requires casting.
    employee.StreetLine2 = (string)setDataRecordSafely(dataRecord, "StreetLine2");
    // Other fields omitted for brevity...
    return employee;
}

这是我编写的用于处理 NULL 值的方法。 这由上面显示的第二个解决方法调用。

public static object setDataRecordSafely(IDataRecord dataRecord, string fieldName)
{
    int fieldIndex = dataRecord.GetOrdinal(fieldName);
    bool isFieldNull = dataRecord.IsDBNull(fieldIndex);
    Type fieldType = dataRecord.GetFieldType(fieldIndex);
    switch (Type.GetTypeCode(fieldType))
    {
        case TypeCode.String:
            return isFieldNull ? "" : dataRecord.GetString(fieldIndex);
        case TypeCode.Int32:
            return isFieldNull ? 0 : dataRecord.GetInt32(fieldIndex);
        case TypeCode.Boolean:
            return isFieldNull ? false : dataRecord.GetBoolean(fieldIndex);
        // TODO: Extend to handle other types as required.
    }
    return null; // The type wasn't handled.
}

有没有办法重载 setDataRecordSafely() 方法来返回适当的 System.Type,这样我就不必强制转换返回的值? 我想避免的是 FillDataRecord() 方法中的这种类型的转换。

employee.StreetLine2 = (string)setDataRecordSafely(dataRecord, "StreetLine2");
employee.City = (string)setDataRecordSafely(dataRecord, "City");
employee.StateID = (int)setDataRecordSafely(dataRecord, "StateID");

或者,在通过 IDataReader 分配列值时,是否有更好的方法来处理 NULL? 感谢您的帮助。

=== 编辑 12/31/2014 下午 1:45 中部 ===

谢谢@Rhumborl和@Jeff梅尔卡多。我使用您建议的扩展方法运行,我的解决方案如下。 您提供给杰夫的第 2 段指导的道具。 这是我的课。

public static class IDataRecordExtensions
{
    /// <summary>
    /// Extension that gets the field's string value, or transforms null into an empty string.
    /// </summary>
    public static string GetString(this IDataRecord dataRecord, string fieldName)
    {
        int fieldIndex = dataRecord.GetOrdinal(fieldName);
        bool isFieldNull = dataRecord.IsDBNull(fieldIndex);
        return isFieldNull ? string.Empty : dataRecord.GetString(fieldIndex);
    }
    /// <summary>
    /// Extension that gets the field's int value, or transforms null into 0.
    /// </summary>
    public static int GetInt32(this IDataRecord dataRecord, string fieldName)
    {
        int fieldIndex = dataRecord.GetOrdinal(fieldName);
        bool isFieldNull = dataRecord.IsDBNull(fieldIndex);
        return isFieldNull ? 0 : dataRecord.GetInt32(fieldIndex);
    }
    /// <summary>
    /// Extension that gets the field's bool value, or transforms null into false.
    /// </summary>
    public static bool GetBoolean(this IDataRecord dataRecord, string fieldName)
    {
        int fieldIndex = dataRecord.GetOrdinal(fieldName);
        bool isFieldNull = dataRecord.IsDBNull(fieldIndex);
        return isFieldNull ? false : dataRecord.GetBoolean(fieldIndex);
    }
}

这是通过 using 语句导入类命名空间后的实现。

public static Employee FillDataRecord(IDataRecord dataRecord)
{
    Employee employee = new BusinessEntities.Employee();
    employee.EmployeeID = dataRecord.GetInt32("EmployeeID");
    employee.FirstName = dataRecord.GetString("FirstName");
    employee.LastName = dataRecord.GetString("LastName");
    employee.Title = dataRecord.GetString("Title");
    employee.Email = dataRecord.GetString("Email");
    employee.StreetLine1 = dataRecord.GetString("StreetLine1");
    employee.StreetLine2 = dataRecord.GetString("StreetLine2");
    employee.City = dataRecord.GetString("City");
    employee.StateID = dataRecord.GetInt32("StateID");
    employee.ZipCode = dataRecord.GetString("ZipCode");
    employee.CountryID = dataRecord.GetInt32("CountryID");
    employee.IsDeleted = dataRecord.GetBoolean("IsDeleted");
    // Above is in lieu of this syntax, which doesn't handle null.
    // employee.Email = dataRecord.GetString(dataRecord.GetOrdinal("Email"));
    return employee;
}

在分配 IDataRecord 值时,需要一种简洁的方法来评估 NULL

在这种情况下,使用扩展方法和使用适当的名称可以产生重大影响。

setDataRecordSafely()是一个令人困惑的名字。 这意味着您正在设置一个值,但实际上您正在获得一个值。 不要尝试像那里那样尝试处理所有案例的方法,而是为您想要使用适当类型支持的所有案例创建一个。 如果您只是返回object,您将不会获得类型安全,您应该返回最合适的类型。

幸运的是,IDataRecord 接口提供了一种通用的GetValue()方法,您可以使用该方法而不是使用专门类型的 getter。 因此可以推广为通用方法。

public static T TryGetValue<T>(this IDataRecord record, string fieldName, T defaultValue = default(T))
{
    try
    {
        var index = record.GetOrdinal(fieldName);
        return !record.IsDBNull(index) ? (T)record.GetValue(index) : defaultValue;
    }
    catch // or do type/data checking
    {
        return defaultValue;
    }
}

如果类型的默认值不够好,则可以添加额外的方法来输入首选默认值。

public static string TryGetString(this IDataRecord record, string fieldName, string defaultValue = "")
{
    return TryGetValue(record, fieldName, defaultValue);
}
public static int TryGetInt32(this IDataRecord record, string fieldName, int defaultValue = 0)
{
    return TryGetValue(record, fieldName, defaultValue);
}

现在,您的调用代码可以简单地执行此操作:

employee.StreetLine1 = dataRecord.TryGetString("StreetLine1");
employee.StreetLine2 = dataRecord.TryGetValue<string>("StreetLine2", ""); // or the generic version
employee.City = dataRecord.TryGetValue<string>("City"); // or use the default value
employee.StateID = dataRecord.TryGetInt32("StateID");
// and so on...