DataContext.GetTable<TEntity>() 是開發者最常呼叫的方法,其背後涉及強型別初始化、MetaModel 映射、反射建構與快取機制等關鍵環節。
若 DataContext 子類別定義了 Table<Entity> 欄位,InitTables() 會透過反射自動填充:
private void InitTables(object schema)
{
foreach (FieldInfo info in schema.GetType().GetFields(
BindingFlags.Public | BindingFlags.Instance))
{
Type fieldType = info.FieldType;
if (fieldType.IsGenericType &&
fieldType.GetGenericTypeDefinition() == typeof(Table<>) &&
((ITable)info.GetValue(schema)) == null)
{
Type entityType = fieldType.GetGenericArguments()[0];
ITable table = this.GetTable(entityType);
info.SetValue(schema, table);
}
}
}
此機制僅在強型別情境下啟動,預設 DataContext 輕量無此開銷。
public Table<TEntity> GetTable<TEntity>() where TEntity : class
{
this.CheckDispose();
MetaTable metaTable = this.services.Model.GetTable(typeof(TEntity));
if (metaTable == null)
throw Error.TypeIsNotMarkedAsTable(typeof(TEntity));
ITable table = this.GetTable(metaTable);
if (table.ElementType != typeof(TEntity))
throw Error.CouldNotGetTableForSubtype(typeof(TEntity), metaTable.RowType.Type);
return (Table<TEntity>)table;
}
private ITable GetTable(MetaTable metaTable)
{
ITable table;
if (!this.tables.TryGetValue(metaTable, out table))
{
ValidateTable(metaTable);
table = (ITable)Activator.CreateInstance(
typeof(Table<>).MakeGenericType(metaTable.RowType.Type),
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, new object[] { this, metaTable }, null);
this.tables.Add(metaTable, table);
}
return table;
}
MetaTableTable<T> 建構子為私有,且泛型參數執行期才確定,故必須用 Activator.CreateInstance 配合 MakeGenericType 建構Dictionary<MetaTable, ITable> 避免重複反射開銷,形成變體單例模式MetaModel 定義 GetTable、GetFunction、GetMetaType 等建構方法:
AttributeMetaModel → 建構 AttributeMetaTable、AttributeMetaFunction...MappedMetaModel (Xml) → 建構 MappedMetaTable、MappedMetaFunction...
確保同一映射模式下建構的產品屬於同一家族,避免混用 Attribute 與 Xml 映射產品造成不一致。