我有一个 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;
}
Run Code Online (Sandbox Code Playgroud)
这是我编写的用于处理 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.
}
Run Code Online (Sandbox Code Playgroud)
有没有办法重载 setDataRecordSafely() 方法以返回适当的 System.Type,这样我就不必强制转换返回的值?我想避免的是 FillDataRecord() 方法中的这种类型的转换。
employee.StreetLine2 = (string)setDataRecordSafely(dataRecord, "StreetLine2");
employee.City = (string)setDataRecordSafely(dataRecord, "City");
employee.StateID = (int)setDataRecordSafely(dataRecord, "StateID");
Run Code Online (Sandbox Code Playgroud)
或者,在通过 IDataReader 分配列值时是否有更好的方法来处理 NULL?感谢您的帮助。
=== 编辑 2014 年 12 月 31 日下午 1:45 中部 ===
谢谢@Rhumborl 和@Jeff Mercado。我按照您建议的扩展方法运行,我的解决方案如下。支持 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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是通过 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;
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,使用扩展方法和使用适当的名称可以发挥重要作用。
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;
}
}
Run Code Online (Sandbox Code Playgroud)
如果类型的默认值不够好,您可以添加额外的方法来输入您首选的默认值。
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);
}
Run Code Online (Sandbox Code Playgroud)
现在您的调用代码可以简单地执行以下操作:
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...
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1247 次 |
| 最近记录: |