自動產生的 InnerReadRecord 利用 FieldIndexs 映射表 (陣列索引 → reader 欄位序號),將資料從 IDataReader 複製到實體屬性。GetDefaultValue 取得屬性預設值,GetValueString 將值轉為 C# 程式碼常值表示。
private PropertyInfo[] GetBindProperties(Type recordType)
{
if (recordType == null) throw new ArgumentNullException("recordType");
if (!recordType.IsPublic) throw new ArgumentException($"類型 {recordType.FullName} 不是公開類型");
if (!recordType.IsClass) throw new ArgumentException($"類型 {recordType.FullName} 不是類別");
if (recordType.IsAbstract) throw new ArgumentException($"類型 {recordType.FullName} 不得是抽象類別");
if (recordType.GetConstructor(Type.EmptyTypes) == null)
throw new ArgumentException($"類型 {recordType.FullName} 沒有預設建構函式");
var list = new ArrayList();
PropertyInfo[] props = recordType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo p in props)
{
var attr = (BindFieldAttribute)Attribute.GetCustomAttribute(p, typeof(BindFieldAttribute));
if (attr != null)
{
if (p.GetIndexParameters().Length > 0)
throw new ArgumentException($"屬性 {recordType.FullName}.{p.Name} 不得有參數");
Type pt = p.PropertyType;
if (pt.IsPrimitive || pt == typeof(string) || pt == typeof(DateTime))
list.Add(p);
else
throw new ArgumentException($"不支援屬性 {recordType.FullName}.{p.Name} 的資料類型 {pt.FullName}");
}
}
if (list.Count == 0)
throw new ArgumentException($"類型 {recordType.FullName} 沒有標記為繫結到任何欄位");
return (PropertyInfo[])list.ToArray(typeof(PropertyInfo));
}
| 檢查項目 | 失敗時例外 |
|---|---|
| 型別為 null | ArgumentNullException |
| 非 public | ArgumentException |
| 非 class (如 struct/interface) | ArgumentException |
| abstract | ArgumentException |
| 無 public 無參建構函式 | ArgumentException |
| 屬性為索引子 | ArgumentException |
| 屬性型別不在白名單 | ArgumentException |
| 無任何 BindFieldAttribute 屬性 | ArgumentException |
取得 PropertyInfo[] 後,框架為每個屬性產生對應的 ConvertToXxx 呼叫程式碼 (見第 8 部分),最終編譯成強型別 InnerReadRecord 方法。