手寫 GetModel 將 DataRow 逐欄位賦值給實體類別,重複、易錯、維護困難。
public class ModelHelper<T> where T : new()
{
public static T ConvertModel(DataRow dr)
{
T model = new T();
Type type = model.GetType();
foreach (PropertyInfo prop in type.GetProperties())
{
object value = dr[prop.Name];
prop.SetValue(model, GetDefaultValue(value, prop.PropertyType), null);
}
return model;
}
private static object GetDefaultValue(object value, Type targetType)
{
if (value == DBNull.Value) return null; // 或 targetType.IsValueType ? Activator.CreateInstance(targetType) : null
return Convert.ChangeType(value, targetType);
}
}
var model = ModelHelper<UserInfo>.ConvertModel(dataRow);
where T : new():確保有無參數建構函式。ColumnAttribute 映射。Convert.ChangeType 不接受 DBNull,必須先轉為 null 或預設值。Convert.ChangeType 支援基本型別、DateTime, Guid, Enum 等;自訂型別需自行擴充。| 限制 | 改進方案 |
|---|---|
| 屬性名必須與欄位名完全一致 | 引入 [Column("db_column")] Attribute,讀取映射名 |
| 不支援巢狀物件/複雜型別 | 遞迴處理屬性型別為 Class 的情況 |
| 唯讀屬性 (無 setter) 會拋例外 | 檢查 prop.CanWrite 跳過 |
| 效能:反射每次調用開銷 | 緩存 PropertyInfo[]、使用 Expression.Compile 生成強型別委派、或採用 FastMember/Dapper |
connection.Query<T>(sql) 內建高效映射。ModelHelper<T> 以 20 行代碼解決 80% 簡單場景的 DataRow→Model 需求,展現反射的威力。理解其原理與限制,能在選型時做出正確決策:小專案直用、中專案擴充映射屬性、大專案上成熟 ORM/Mapper。