在分配 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;
}
在这种情况下,使用扩展方法和使用适当的名称可以产生重大影响。
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...