简体   繁体   English

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

[英]Need a concise way to evaluate NULL when assigning IDataRecord values

I have a FillDataRecord method that assigns values to an object from an IDataRecord. 我有一个FillDataRecord方法,该方法将值分配给IDataRecord中的对象。 It works until it encounters a field with a NULL value, at which point it breaks with the message, "Data is Null. This method or property cannot be called on Null values." 它会一直工作到遇到带有NULL值的字段为止,这时它会中断消息“ Data is Null。此方法或属性不能在Null值上调用”。

The workaround is to use IDataRecord.IsDBNull, which I've done, but I'd like to make this cleaner. 解决方法是使用IDataRecord.IsDBNull(已完成),但我想使其更整洁。 Here is the code with some relevant comments. 这是带有一些相关注释的代码。

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;
}

Here is the method that I wrote to handle NULL values. 这是我编写的用于处理NULL值的方法。 This gets called by the second workaround shown above. 上面显示的第二个解决方法调用了此方法。

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.
}

Is there a way to overload that setDataRecordSafely() method to return the appropriate System.Type so that I don't have to cast the returned value? 有没有一种方法可以重载setDataRecordSafely()方法以返回适当的System.Type,这样我就不必强制转换返回的值了? What I'd like to avoid is this type of casting within the FillDataRecord() method. 我要避免的是FillDataRecord()方法中的这种类型的转换。

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

Or, is there a better way to handle NULL when assigning column values via an IDataReader? 或者,当通过IDataReader分配列值时,有没有更好的方法来处理NULL? Thanks for your help. 谢谢你的帮助。

=== Edit 12/31/2014 at 1:45 PM Central === ===编辑2014年12月31日下午1:45 =

Thanks @Rhumborl and @Jeff Mercado. 感谢@Rhumborl和@Jeff Mercado。 I ran with the Extension Methods you suggested, and my solution is below. 我运行了您建议的扩展方法,下面是我的解决方案。 Props for the paragraph 2 guidance you provided Jeff. 您为Jeff提供的第2段指南的道具。 Here is my class. 这是我的课。

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);
    }

}

And here is the implementation, after importing the class namespace via a using statement. 这是通过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;
}

This is a case where using extension methods and using appropriate names can make all the difference. 在这种情况下,使用扩展方法和使用适当的名称可能会有所不同。

setDataRecordSafely() is a confusing name. setDataRecordSafely()是一个令人困惑的名称。 It implies that you are setting a value but you are actually getting a value. 这意味着您正在设置一个值,但实际上是在获取一个值。 Don't try to make a try-to-handle-all-cases method like you have there, make one for all the cases you want to support with the appropriate types. 不要像在那儿那样尝试尝试处理所有情况的方法,而要为要使用适当类型支持的所有情况做一个。 You're not going to get type safety if you are simply returning object , you should return the most appropriate type. 如果只返回object ,就不会获得类型安全,您应该返回最合适的类型。

Fortunately, the IDataRecord interface offers a general GetValue() method, you can use that instead of using the specifically typed getters. 幸运的是, IDataRecord接口提供了一个通用的GetValue()方法,您可以使用它而不是使用专门键入的getter。 And therefore can be generalized as a generic method. 因此可以概括为一种通用方法。

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;
    }
}

If the default value for the type is not good enough, you can add extra methods to feed in your preferred default values. 如果类型的默认值不够好,则可以添加其他方法来输入首选的默认值。

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);
}

Now your calling code can simply do this: 现在,您的调用代码可以简单地做到这一点:

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...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM