NewLife/NewLife.XCode

创建TableItem后,合并连接下的文件模型,测试通过
大石头 编写于 2024-05-03 01:07:26
共计: 修改16个文件,增加199行、删除118行。
重命名 +0 -0
Doc/XCode.emmx → Doc/插入字段.emmx
增加 +0 -0
修改 +8 -15
修改 +5 -14
修改 +116 -31
修改 +3 -3
修改 +7 -7
修改 +8 -8
修改 +29 -24
修改 +1 -1
修改 +1 -1
修改 +1 -1
修改 +1 -1
修改 +1 -1
修改 +8 -9
修改 +10 -2
重命名 +0 -0
Doc/XCode.emmx → Doc/插入字段.emmx
diff --git a/Doc/XCode.emmx "b/Doc/\346\217\222\345\205\245\345\255\227\346\256\265.emmx"
similarity index 100%
rename from Doc/XCode.emmx
rename to "Doc/\346\217\222\345\205\245\345\255\227\346\256\265.emmx"
Binary files a/Doc/XCode.emmx and "b/Doc/\346\217\222\345\205\245\345\255\227\346\256\265.emmx" differ
增加 +0 -0
diff --git "a/Doc/\346\225\260\346\215\256\346\250\241\345\236\213.emmx" "b/Doc/\346\225\260\346\215\256\346\250\241\345\236\213.emmx"
new file mode 100644
index 0000000..6b2053c
Binary files /dev/null and "b/Doc/\346\225\260\346\215\256\346\250\241\345\236\213.emmx" differ
修改 +8 -15
diff --git a/XCode/Attributes/BindTableAttribute.cs b/XCode/Attributes/BindTableAttribute.cs
index e001724..9a9d9dc 100644
--- a/XCode/Attributes/BindTableAttribute.cs
+++ b/XCode/Attributes/BindTableAttribute.cs
@@ -6,27 +6,23 @@ namespace XCode;
 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
 public sealed class BindTableAttribute : Attribute
 {
-    /// <summary>
-    /// 表名。
-    /// 可以在配置文件中通过XCode.ConnMaps把实体映射到别的数据表上
-    /// </summary>
+    /// <summary>表名</summary>
     public String Name { get; set; }
 
     /// <summary>描述</summary>
     public String? Description { get; set; }
 
-    /// <summary>
-    /// 连接名。
+    /// <summary>连接名</summary>
+    /// <remarks>
     /// 实体类的所有数据库操作,将发生在该连接名指定的数据库连接上。
     /// 此外,可动态修改实体类在当前线程上的连接名(改Meta.ConnName);
-    /// 也可以在配置文件中通过XCode.ConnMaps把连接名映射到别的连接上。
-    /// </summary>
+    /// </remarks>
     public String? ConnName { get; set; }
 
-    /// <summary>
-    /// 数据库类型。
+    /// <summary>数据库类型</summary>
+    /// <remarks>
     /// 仅用于记录实体类由何种类型数据库生成,当且仅当目标数据库同为该数据库类型时,采用实体属性信息上的RawType作为反向工程的目标字段类型,以期获得开发和生产的最佳兼容。
-    /// </summary>
+    /// </remarks>
     public DatabaseType DbType { get; set; }
 
     /// <summary>是否视图</summary>
@@ -34,10 +30,7 @@ public sealed class BindTableAttribute : Attribute
 
     /// <summary>构造函数</summary>
     /// <param name="name">表名</param>
-    public BindTableAttribute(String name)
-    {
-        Name = name;
-    }
+    public BindTableAttribute(String name) => Name = name;
 
     /// <summary>构造函数</summary>
     /// <param name="name">表名</param>
修改 +5 -14
diff --git a/XCode/Configuration/FieldItem.cs b/XCode/Configuration/FieldItem.cs
index 4574e41..9015243 100644
--- a/XCode/Configuration/FieldItem.cs
+++ b/XCode/Configuration/FieldItem.cs
@@ -45,7 +45,7 @@ public class FieldItem
             var name = Description;
             if (name.IsNullOrEmpty()) return Name;
 
-            var p = name.IndexOfAny(new[] { '。', ',' });
+            var p = name.IndexOfAny(['。', ',']);
             if (p > 0) name = name[..p];
 
             return name;
@@ -84,7 +84,7 @@ public class FieldItem
     public Boolean PrimaryKey { get; internal set; }
 
     /// <summary>是否主字段。主字段作为业务主要字段,代表当前数据行意义</summary>
-    public Boolean Master { get; private set; }
+    public Boolean Master { get; internal set; }
 
     /// <summary>是否允许空</summary>
     public Boolean IsNullable { get; internal set; }
@@ -99,7 +99,7 @@ public class FieldItem
     public Boolean IsDynamic => _Property == null;
 
     /// <summary>字段名要过滤掉的标识符,考虑MSSQL、MySql、SQLite、Oracle等</summary>
-    static readonly Char[] COLUMNNAME_FLAG = new Char[] { '[', ']', '\'', '"', '`' };
+    static readonly Char[] COLUMNNAME_FLAG = ['[', ']', '\'', '"', '`'];
 
     private String? _ColumnName;
     /// <summary>用于数据绑定的字段名</summary>
@@ -113,23 +113,14 @@ public class FieldItem
     /// <remarks>set { _ReadOnly = value; } 放出只读属性的设置,比如在编辑页面的时候,有的字段不能修改 如修改用户时  不能修改用户名</remarks>
     public Boolean ReadOnly { get; set; }
 
-    /// <summary>表</summary>
+    /// <summary>表定义。来自实体类特性</summary>
     public TableItem Table { get; internal protected set; } = null!;
 
     /// <summary>字段</summary>
     public IDataColumn Field { get; private set; } = null!;
 
     /// <summary>实体操作者</summary>
-    public IEntityFactory? Factory
-    {
-        get
-        {
-            var type = Table?.EntityType;
-            if (type == null || type.IsInterface) return null;
-
-            return type.AsFactory();
-        }
-    }
+    public IEntityFactory? Factory => Table?.EntityType.AsFactory();
 
     /// <summary>已格式化的字段名,可字节用于SQL中。主要用于处理关键字,比如MSSQL里面的[User]</summary>
     public String? FormatedName => Field == null ? null : Factory?.Session.Dal.Db.FormatName(Field);
修改 +116 -31
diff --git a/XCode/Configuration/TableItem.cs b/XCode/Configuration/TableItem.cs
index 884ab8f..016feaa 100644
--- a/XCode/Configuration/TableItem.cs
+++ b/XCode/Configuration/TableItem.cs
@@ -22,26 +22,34 @@ public class TableItem
     /// <summary>绑定索引特性</summary>
     private readonly BindIndexAttribute[] _Indexes;
 
+    private String? _description;
     private readonly DescriptionAttribute? _Description;
     /// <summary>说明</summary>
     public String? Description
     {
         get
         {
-            if (_Description != null && !String.IsNullOrEmpty(_Description.Description)) return _Description.Description;
-            if (_Table != null && !String.IsNullOrEmpty(_Table.Description)) return _Table.Description;
+            if (_description != null) return _description;
 
-            return null;
+            if (_Description != null && !String.IsNullOrEmpty(_Description.Description)) return _description = _Description.Description;
+            if (_Table != null && !String.IsNullOrEmpty(_Table.Description)) return _description = _Table.Description;
+
+            return _description;
         }
     }
     #endregion
 
     #region 属性
-    /// <summary>默认表名。来自实体类特性</summary>
-    public String TableName => _Table?.Name ?? EntityType.Name;
+    private String? _tableName;
+    /// <summary>表名。来自实体类特性,合并文件模型</summary>
+    public String TableName => _tableName ??= _Table?.Name ?? EntityType.Name;
+
+    /// <summary>原始表名</summary>
+    public String RawTableName => _Table?.Name ?? EntityType.Name;
 
-    /// <summary>默认连接名。来自实体类特性</summary>
-    public String ConnName => _Table?.ConnName + "";
+    private String? _connName;
+    /// <summary>连接名。来自实体类特性,合并文件模型</summary>
+    public String ConnName => _connName ??= _Table?.ConnName + "";
     #endregion
 
     #region 扩展属性
@@ -137,20 +145,35 @@ public class TableItem
         InitFields();
     }
 
-    private static readonly ConcurrentDictionary<Type, TableItem> _cache = new();
-    /// <summary>创建</summary>
+    private static readonly ConcurrentDictionary<String, TableItem> _cache = new();
+    /// <summary>创建数据表元数据信息。并合并连接名上的文件模型映射</summary>
     /// <param name="type">类型</param>
+    /// <param name="connName">连接名。该类型的架构信息,则不同连接上可能存在文件模型映射,导致不一致</param>
     /// <returns></returns>
-    public static TableItem Create(Type type)
+    public static TableItem Create(Type type, String? connName)
     {
         if (type == null) throw new ArgumentNullException(nameof(type));
 
-        if (_cache.TryGetValue(type, out var tableItem)) return tableItem;
+        var key = $"{type.FullName}#{connName}";
+        if (_cache.TryGetValue(key, out var tableItem)) return tableItem;
 
+        // 不能给没有BindTableAttribute特性的类型创建TableItem,否则可能会在InitFields中抛出异常
         if (type.GetCustomAttribute<BindTableAttribute>(true) == null) throw new ArgumentOutOfRangeException(nameof(type));
 
-        // 不能给没有BindTableAttribute特性的类型创建TableItem,否则可能会在InitFields中抛出异常
-        return _cache.GetOrAdd(type, key => new TableItem(key));
+        // 先创建,然后合并外部文件模型
+        var ti = new TableItem(type);
+
+        if (connName.IsNullOrEmpty()) connName = ti.ConnName;
+        if (!connName.IsNullOrEmpty())
+        {
+            // 根据默认连接名找到目标文件模型,如果存在则合并
+            var table = DAL.Create(connName).ModelTables.FirstOrDefault(e => e.Name == ti.EntityType.Name);
+            if (table != null) ti.Merge(table);
+        }
+
+        ti._connName = connName;
+
+        return _cache.GetOrAdd(key, ti);
     }
 
     private void InitFields()
@@ -334,6 +357,68 @@ public class TableItem
     #endregion
 
     #region 方法
+    /// <summary>合并目标表到当前元数据</summary>
+    /// <param name="table"></param>
+    public void Merge(IDataTable table)
+    {
+        table = (table.Clone() as IDataTable)!;
+        if (table == null) return;
+
+        DataTable = table;
+
+        // 合并字段
+        _tableName = table.TableName;
+        _description = table.Description;
+
+        var allfields = AllFields.ToList();
+        var fields = Fields.ToList();
+        var pkeys = new List<FieldItem>();
+        foreach (var column in table.Columns)
+        {
+            if (column.Name.IsNullOrEmpty()) continue;
+
+            var fi = fields.FirstOrDefault(e => e.Name.EqualIgnoreCase(column.Name));
+            if (fi is null)
+            {
+                fi = new Field(this, column.Name, column.DataType!, column.Description, column.Length)
+                {
+                    ColumnName = column.ColumnName,
+                    IsNullable = column.Nullable,
+                    IsIdentity = column.Identity,
+                    PrimaryKey = column.PrimaryKey,
+                    Master = column.Master,
+                    DisplayName = column.DisplayName,
+                    Description = column.Description,
+                    //Map = column.Map,
+                    IsDataObjectField = true
+                };
+
+                fields.Add(fi);
+
+                if (!allfields.Any(e => e.Name.EqualIgnoreCase(column.Name)))
+                    allfields.Add(fi);
+            }
+            else
+            {
+                fi.ColumnName = column.ColumnName;
+                fi.IsNullable = column.Nullable;
+                fi.IsIdentity = column.Identity;
+                fi.PrimaryKey = column.PrimaryKey;
+                fi.Master = column.Master;
+                fi.DisplayName = column.DisplayName;
+                fi.Description = column.Description;
+            }
+
+            if (fi.PrimaryKey) pkeys.Add(fi);
+            if (fi.IsIdentity) Identity = fi;
+            if (fi.Master) Master = fi;
+        }
+
+        Fields = fields.ToArray();
+        AllFields = allfields.ToArray();
+        PrimaryKeys = pkeys.ToArray();
+    }
+
     private IDictionary<String, Field?> _all;
 
     /// <summary>根据名称查找</summary>
@@ -398,28 +483,28 @@ public class TableItem
     #endregion
 
     #region 动态增加字段
-    /// <summary>动态增加字段</summary>
-    /// <param name="name"></param>
-    /// <param name="type"></param>
-    /// <param name="description"></param>
-    /// <param name="length"></param>
-    /// <returns></returns>
-    public TableItem Add(String name, Type type, String? description = null, Int32 length = 0)
-    {
-        var f = new Field(this, name, type, description, length);
+    ///// <summary>动态增加字段</summary>
+    ///// <param name="name"></param>
+    ///// <param name="type"></param>
+    ///// <param name="description"></param>
+    ///// <param name="length"></param>
+    ///// <returns></returns>
+    //public TableItem Add(String name, Type type, String? description = null, Int32 length = 0)
+    //{
+    //    var f = new Field(this, name, type, description, length);
 
-        var list = new List<FieldItem>(Fields) { f };
-        Fields = list.ToArray();
+    //    var list = new List<FieldItem>(Fields) { f };
+    //    Fields = list.ToArray();
 
-        list = new List<FieldItem>(AllFields) { f };
-        AllFields = list.ToArray();
+    //    list = new List<FieldItem>(AllFields) { f };
+    //    AllFields = list.ToArray();
 
-        var dc = DataTable.CreateColumn();
-        f.Fill(dc);
+    //    var dc = DataTable.CreateColumn();
+    //    f.Fill(dc);
 
-        DataTable.Columns.Add(dc);
+    //    DataTable.Columns.Add(dc);
 
-        return this;
-    }
+    //    return this;
+    //}
     #endregion
 }
\ No newline at end of file
修改 +3 -3
diff --git a/XCode/Entity/Entity_Meta.cs b/XCode/Entity/Entity_Meta.cs
index c09861b..0169350 100644
--- a/XCode/Entity/Entity_Meta.cs
+++ b/XCode/Entity/Entity_Meta.cs
@@ -38,9 +38,9 @@ public partial class Entity<TEntity>
         #endregion
 
         #region 基本属性
-        private static readonly Lazy<TableItem> _Table = new(() => TableItem.Create(ThisType));
-        /// <summary>表信息。来自实体类</summary>
-        public static TableItem Table => _Table.Value;
+        private static TableItem? _Table;
+        /// <summary>数据表元数据信息。来自实体类,并合并默认连接名上的文件模型</summary>
+        public static TableItem Table { get => _Table ??= TableItem.Create(ThisType, null); set => _Table = value; }
 
 #if NET45
         private static readonly ThreadLocal<String?> _ConnName = new();
修改 +7 -7
diff --git a/XCode/Entity/EntityExtension.cs b/XCode/Entity/EntityExtension.cs
index ee5ba9a..ab6a93f 100644
--- a/XCode/Entity/EntityExtension.cs
+++ b/XCode/Entity/EntityExtension.cs
@@ -268,7 +268,7 @@ public static class EntityExtension
     {
         // 没有其它唯一索引,且主键为空时,走批量插入
         var rs = 0;
-        if (!session.Table.Indexes.Any(di => di.Unique))
+        if (!session.DataTable.Indexes.Any(di => di.Unique))
         {
             var inserts = new List<T>();
             var updates = new List<T>();
@@ -501,7 +501,7 @@ public static class EntityExtension
         using var span = tracer?.NewSpan($"db:{dal.ConnName}:BatchInsert:{fact.Table.TableName}", $"{session.TableName}[{list.Count()}]", list.Count());
         try
         {
-            var rs = dal.Session.Insert(session.Table, option.Columns, list.Cast<IModel>());
+            var rs = dal.Session.Insert(session.DataTable, option.Columns, list.Cast<IModel>());
 
             // 清除脏数据,避免重复提交保存
             foreach (var item in list)
@@ -583,7 +583,7 @@ public static class EntityExtension
         {
             //if (span != null && list is ICollection collection) span.Tag = $"{session.TableName}[{collection.Count}]";
 
-            var rs = dal.Session.InsertIgnore(session.Table, option.Columns, list.Cast<IModel>());
+            var rs = dal.Session.InsertIgnore(session.DataTable, option.Columns, list.Cast<IModel>());
 
             // 清除脏数据,避免重复提交保存
             foreach (var item in list)
@@ -665,7 +665,7 @@ public static class EntityExtension
         {
             //if (span != null && list is ICollection collection) span.Tag = $"{session.TableName}[{collection.Count}]";
 
-            var rs = dal.Session.Replace(session.Table, option.Columns, list.Cast<IModel>());
+            var rs = dal.Session.Replace(session.DataTable, option.Columns, list.Cast<IModel>());
 
             // 清除脏数据,避免重复提交保存
             foreach (var item in list)
@@ -749,7 +749,7 @@ public static class EntityExtension
         {
             //if (span != null && list is ICollection collection) span.Tag = $"{session.TableName}[{collection.Count}]";
 
-            var rs = dal.Session.Update(session.Table, option.Columns, updateColumns, addColumns, list.Cast<IModel>());
+            var rs = dal.Session.Update(session.DataTable, option.Columns, updateColumns, addColumns, list.Cast<IModel>());
 
             // 清除脏数据,避免重复提交保存
             foreach (var item in list)
@@ -888,7 +888,7 @@ public static class EntityExtension
         {
             //if (span != null && list is ICollection collection) span.Tag = $"{session.TableName}[{collection.Count}]";
 
-            var rs = dal.Session.Upsert(session.Table, option.Columns, updateColumns, addColumns, list.Cast<IModel>());
+            var rs = dal.Session.Upsert(session.DataTable, option.Columns, updateColumns, addColumns, list.Cast<IModel>());
 
             // 清除脏数据,避免重复提交保存
             foreach (var item in list)
@@ -972,7 +972,7 @@ public static class EntityExtension
         {
             if (span != null) span.Tag = $"{session.TableName}[{entity}]";
 
-            return dal.Session.Upsert(session.Table, option.Columns, option.UpdateColumns, option.AddColumns, [entity as IModel]);
+            return dal.Session.Upsert(session.DataTable, option.Columns, option.UpdateColumns, option.AddColumns, [entity as IModel]);
         }
         catch (Exception ex)
         {
修改 +8 -8
diff --git a/XCode/Entity/EntityFactory.cs b/XCode/Entity/EntityFactory.cs
index e601b0a..a7f42ac 100644
--- a/XCode/Entity/EntityFactory.cs
+++ b/XCode/Entity/EntityFactory.cs
@@ -110,31 +110,31 @@ public static class EntityFactory
         var dic = new Dictionary<String, Type>(StringComparer.OrdinalIgnoreCase);
         var list = new List<String>();
         var list2 = new List<String>();
-        foreach (var item in LoadEntities(connName))
+        foreach (var type in LoadEntities(connName))
         {
-            list.Add(item.Name);
+            list.Add(type.Name);
 
             // 过滤掉第一次使用才加载的
             if (checkMode)
             {
-                var att = item.GetCustomAttribute<ModelCheckModeAttribute>(true);
+                var att = type.GetCustomAttribute<ModelCheckModeAttribute>(true);
                 if (att != null && att.Mode != ModelCheckModes.CheckAllTablesWhenInit) continue;
             }
-            list2.Add(item.Name);
+            list2.Add(type.Name);
 
-            var table = TableItem.Create(item).DataTable;
+            var table = TableItem.Create(type, connName).DataTable;
 
             // 判断表名是否已存在
-            if (dic.TryGetValue(table.TableName, out var type))
+            if (dic.TryGetValue(table.TableName, out var oldType))
             {
                 // 两个都不是,报错吧!
-                var msg = $"设计错误!发现表{table.TableName}同时被两个实体类({type.FullName}和{item.FullName})使用!";
+                var msg = $"设计错误!发现表{table.TableName}同时被两个实体类({oldType.FullName}和{type.FullName})使用!";
                 XTrace.WriteLine(msg);
                 throw new XCodeException(msg);
             }
             else
             {
-                dic.Add(table.TableName, item);
+                dic.Add(table.TableName, type);
             }
 
             tables.Add(table);
修改 +29 -24
diff --git a/XCode/Entity/EntitySession.cs b/XCode/Entity/EntitySession.cs
index bb6bc6f..06d3f9e 100644
--- a/XCode/Entity/EntitySession.cs
+++ b/XCode/Entity/EntitySession.cs
@@ -75,13 +75,18 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
         TableName = tableName;
         Key = connName + "###" + tableName;
 
-        TableItem = TableItem.Create(ThisType);
-        Table = (TableItem.DataTable.Clone() as IDataTable)!;
-        Table.TableName = tableName;
+        var ti = Table = TableItem.Create(ThisType, connName);
+        DataTable = (Table.DataTable.Clone() as IDataTable)!;
+
+        // 使用文件模型映射的表名
+        if (tableName == ti.RawTableName)
+            TableName = DataTable.TableName;
+        else
+            DataTable.TableName = tableName;
 
         Queue = new EntityQueue(this)
         {
-            InsertOnly = Table.InsertOnly
+            InsertOnly = DataTable.InsertOnly
         };
     }
 
@@ -113,11 +118,11 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     #region 主要属性
     private Type ThisType => typeof(TEntity);
 
-    /// <summary>表信息</summary>
-    TableItem TableItem { get; }
+    /// <summary>数据表元数据信息。合并连接上的文件模型,可能跟Meta.Table不一致</summary>
+    public TableItem Table { get; }
 
     /// <summary>数据表</summary>
-    public IDataTable Table { get; }
+    public IDataTable DataTable { get; }
 
     IEntityFactory Factory { get; } = typeof(TEntity).AsFactory();
 
@@ -132,7 +137,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     {
         get
         {
-            if (_FormatedTableName.IsNullOrEmpty()) _FormatedTableName = Dal.Db.FormatName(Table);
+            if (_FormatedTableName.IsNullOrEmpty()) _FormatedTableName = Dal.Db.FormatName(DataTable);
 
             return _FormatedTableName;
         }
@@ -146,8 +151,8 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
         {
             if (_Default != null) return _Default;
 
-            var cname = TableItem.ConnName;
-            var tname = TableItem.TableName;
+            var cname = Table.ConnName;
+            var tname = Table.TableName;
 
             if (ConnName == cname && TableName == tname)
                 _Default = this;
@@ -174,7 +179,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     public Boolean WaitForInitData(Int32 ms = 3000)
     {
         // 已初始化
-        if (hasCheckInitData || Table.IsView) return true;
+        if (hasCheckInitData || DataTable.IsView) return true;
 
         var tid = Thread.CurrentThread.ManagedThreadId;
 
@@ -232,7 +237,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
         var dal = Dal;
 
         // 检查新表名对应的数据表
-        var table = TableItem.DataTable;
+        var table = Table.DataTable;
         // 克隆一份,防止修改
         table = (table.Clone() as IDataTable)!;
 
@@ -254,7 +259,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     /// <summary>检查模型。依据反向工程设置、是否首次使用检查、是否已常规检查等</summary>
     private void CheckModel()
     {
-        if (_hasCheckModel || Table.IsView) return;
+        if (_hasCheckModel || DataTable.IsView) return;
         lock (_checkLock)
         {
             if (_hasCheckModel) return;
@@ -269,7 +274,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
             }
 
             // CheckTableWhenFirstUse的实体类,在这里检查,有点意思,记下来
-            var mode = TableItem.ModelCheckMode;
+            var mode = Table.ModelCheckMode;
             if (DAL.Debug && mode == ModelCheckModes.CheckTableWhenFirstUse)
                 DAL.WriteLog("检查实体{0}的数据表架构,模式:{1}", ThisType.FullName, mode);
 
@@ -446,12 +451,12 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
         var dal = Dal;
 
         // 第一次访问,SQLite的Select Count非常慢,数据大于阀值时,使用最大ID作为表记录数
-        if (count < 0 && dal.DbType == DatabaseType.SQLite && TableItem.Identity != null)
+        if (count < 0 && dal.DbType == DatabaseType.SQLite && Table.Identity != null)
         {
             var builder = new SelectBuilder
             {
                 Table = FormatedTableName,
-                OrderBy = TableItem.Identity.Desc()
+                OrderBy = Table.Identity.Desc()
             };
             FixBuilder(builder);
             var ds = dal.Query(builder, 0, 1);
@@ -460,7 +465,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
         }
 
         // 100w数据时,没有预热Select Count需要3000ms,预热后需要500ms
-        if (dal.DbType != DatabaseType.SQLite && (count <= 0 || count >= 1_000_000) && !Table.IsView)
+        if (dal.DbType != DatabaseType.SQLite && (count <= 0 || count >= 1_000_000) && !DataTable.IsView)
         {
             using var span = dal.Tracer?.NewSpan($"db:{ConnName}:QueryCountFast:{TableName}");
             try
@@ -607,7 +612,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
 
     private void FixBuilder(SelectBuilder builder)
     {
-        if (Table.IsView && builder.Table == FormatedTableName)
+        if (DataTable.IsView && builder.Table == FormatedTableName)
         {
             builder.Table = $"({Factory.Template.GetSql(Dal.DbType)}) SourceTable";
         }
@@ -812,7 +817,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     /// <returns></returns>
     public virtual Int32 Insert(IEntity entity)
     {
-        if (Table.IsView) throw new NotSupportedException("视图无法添删改!");
+        if (DataTable.IsView) throw new NotSupportedException("视图无法添删改!");
 
         var rs = Factory.Persistence.Insert(this, entity);
 
@@ -836,7 +841,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     /// <returns></returns>
     public virtual Int32 Update(IEntity entity)
     {
-        if (Table.IsView) throw new NotSupportedException("视图无法添删改!");
+        if (DataTable.IsView) throw new NotSupportedException("视图无法添删改!");
 
         var rs = Factory.Persistence.Update(this, entity);
 
@@ -859,7 +864,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     /// <returns></returns>
     public virtual Int32 Delete(IEntity entity)
     {
-        if (Table.IsView) throw new NotSupportedException("视图无法添删改!");
+        if (DataTable.IsView) throw new NotSupportedException("视图无法添删改!");
 
         var rs = Factory.Persistence.Delete(this, entity);
 
@@ -885,7 +890,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     /// <returns></returns>
     public virtual async Task<Int32> InsertAsync(IEntity entity)
     {
-        if (Table.IsView) throw new NotSupportedException("视图无法添删改!");
+        if (DataTable.IsView) throw new NotSupportedException("视图无法添删改!");
 
         var rs = await Factory.Persistence.InsertAsync(this, entity);
 
@@ -905,7 +910,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     /// <returns></returns>
     public virtual Task<Int32> UpdateAsync(IEntity entity)
     {
-        if (Table.IsView) throw new NotSupportedException("视图无法添删改!");
+        if (DataTable.IsView) throw new NotSupportedException("视图无法添删改!");
 
         var rs = Factory.Persistence.UpdateAsync(this, entity);
 
@@ -925,7 +930,7 @@ public class EntitySession<TEntity> : DisposeBase, IEntitySession where TEntity 
     /// <returns></returns>
     public virtual Task<Int32> DeleteAsync(IEntity entity)
     {
-        if (Table.IsView) throw new NotSupportedException("视图无法添删改!");
+        if (DataTable.IsView) throw new NotSupportedException("视图无法添删改!");
 
         var rs = Factory.Persistence.DeleteAsync(this, entity);
 
修改 +1 -1
diff --git a/XCode/Entity/IEntitySession.cs b/XCode/Entity/IEntitySession.cs
index d1eec40..6780f5f 100644
--- a/XCode/Entity/IEntitySession.cs
+++ b/XCode/Entity/IEntitySession.cs
@@ -16,7 +16,7 @@ public interface IEntitySession
     String TableName { get; }
 
     /// <summary>数据表</summary>
-    IDataTable Table { get; }
+    IDataTable DataTable { get; }
 
     /// <summary>用于标识会话的键值</summary>
     String Key { get; }
修改 +1 -1
diff --git a/XCode/Model/FieldExpression.cs b/XCode/Model/FieldExpression.cs
index 8c5111f..087df90 100644
--- a/XCode/Model/FieldExpression.cs
+++ b/XCode/Model/FieldExpression.cs
@@ -50,7 +50,7 @@ public class FieldExpression : Expression
         if (Field == null) return;
 
         // 部分场景外部未能传入数据库,此时内部尽力获取
-        db ??= Field?.Factory.Session.Dal.Db;
+        db ??= Field?.Factory?.Session.Dal.Db ?? throw new ArgumentNullException(nameof(db));
 
         var columnName = db.FormatName(Field.Field);
         if (Action.IsNullOrEmpty())
修改 +1 -1
diff --git a/XCode/Model/FormatExpression.cs b/XCode/Model/FormatExpression.cs
index 21203ee..0cdea4a 100644
--- a/XCode/Model/FormatExpression.cs
+++ b/XCode/Model/FormatExpression.cs
@@ -51,7 +51,7 @@ public class FormatExpression : Expression
         if (Field == null || Format.IsNullOrWhiteSpace()) return;
 
         // 部分场景外部未能传入数据库,此时内部尽力获取
-        if (db == null) db = Field?.Factory.Session.Dal.Db;
+        db ??= Field?.Factory?.Session.Dal.Db ?? throw new ArgumentNullException(nameof(db));
 
         var columnName = db.FormatName(Field.Field);
 
修改 +1 -1
diff --git a/XCode/Model/InExpression.cs b/XCode/Model/InExpression.cs
index 0477f67..64a6206 100644
--- a/XCode/Model/InExpression.cs
+++ b/XCode/Model/InExpression.cs
@@ -52,7 +52,7 @@ namespace XCode
             if (Field == null || Format.IsNullOrWhiteSpace()) return;
 
             // 部分场景外部未能传入数据库,此时内部尽力获取
-            if (db == null) db = Field?.Factory.Session.Dal.Db;
+            db ??= Field?.Factory?.Session.Dal.Db ?? throw new ArgumentNullException(nameof(db));
 
             var column = Field.Field;
             var columnName = db.FormatName(Field.Field);
修改 +1 -1
diff --git a/XCode/Model/LikeExpression.cs b/XCode/Model/LikeExpression.cs
index 38bb38b..6d3704e 100644
--- a/XCode/Model/LikeExpression.cs
+++ b/XCode/Model/LikeExpression.cs
@@ -50,7 +50,7 @@ namespace XCode
             if (Field == null || Format.IsNullOrWhiteSpace()) return;
 
             // 部分场景外部未能传入数据库,此时内部尽力获取
-            if (db == null) db = Field?.Factory.Session.Dal.Db;
+            db ??= Field?.Factory?.Session.Dal.Db ?? throw new ArgumentNullException(nameof(db));
 
             var columnName = db.FormatName(Field.Field);
 
修改 +8 -9
diff --git a/XUnitTest.XCode/Configuration/TableItemTests.cs b/XUnitTest.XCode/Configuration/TableItemTests.cs
index 7308ea2..45753a4 100644
--- a/XUnitTest.XCode/Configuration/TableItemTests.cs
+++ b/XUnitTest.XCode/Configuration/TableItemTests.cs
@@ -3,16 +3,15 @@ using XCode.Configuration;
 using Xunit;
 using XUnitTest.XCode.TestEntity;
 
-namespace XUnitTest.XCode.Configuration
+namespace XUnitTest.XCode.Configuration;
+
+public class TableItemTests
 {
-    public class TableItemTests
+    [Fact]
+    public void TrimIndex()
     {
-        [Fact]
-        public void TrimIndex()
-        {
-            var ti = TableItem.Create(typeof(Log2));
-            XTrace.WriteLine(ti.TableName);
-            Assert.Equal(4, ti.DataTable.Indexes.Count);
-        }
+        var ti = TableItem.Create(typeof(Log2), null);
+        XTrace.WriteLine(ti.TableName);
+        Assert.Equal(4, ti.DataTable.Indexes.Count);
     }
 }
\ No newline at end of file
修改 +10 -2
diff --git a/XUnitTest.XCode/DataAccessLayer/DALTests.cs b/XUnitTest.XCode/DataAccessLayer/DALTests.cs
index d975855..ed8a377 100644
--- a/XUnitTest.XCode/DataAccessLayer/DALTests.cs
+++ b/XUnitTest.XCode/DataAccessLayer/DALTests.cs
@@ -147,6 +147,10 @@ public class DALTests
         // 构建新的模型
         var tb = Role.Meta.Table.DataTable.Clone() as IDataTable;
         tb.TableName = "Role_mtt";
+
+        var column = tb.Columns.First(e => e.Name == "Name");
+        column.ColumnName = "Name_mtt";
+
         var xml = DAL.Export([tb]);
         File.WriteAllText(file.EnsureDirectory(true), xml);
 
@@ -161,7 +165,11 @@ public class DALTests
         using var st = Role.Meta.CreateSplit(name, null);
 
         // 此时我们希望实体类拿到的数据表是新的
-        var tb3 = Role.Meta.Table.DataTable;
-        Assert.Equal(tb.TableName, tb3.TableName);
+        //var tb3 = Role.Meta.Table.DataTable;
+        Assert.Equal(tb.TableName, Role.Meta.Session.Table.TableName);
+        Assert.Equal(tb.TableName, Role.Meta.Session.DataTable.TableName);
+        Assert.Equal(tb.TableName, Role.Meta.Session.TableName);
+        //Assert.Equal(tb.TableName, Role.Meta.TableName);
+        Assert.Equal("Name_mtt", Role.Meta.Session.Table.FindByName("Name").ColumnName);
     }
 }
\ No newline at end of file