diff --git a/Extensions/FreeSql.Extensions.ZeroEntity/FreeSql.Extensions.ZeroEntity.csproj b/Extensions/FreeSql.Extensions.ZeroEntity/FreeSql.Extensions.ZeroEntity.csproj
index 66d0231c..9f181073 100644
--- a/Extensions/FreeSql.Extensions.ZeroEntity/FreeSql.Extensions.ZeroEntity.csproj
+++ b/Extensions/FreeSql.Extensions.ZeroEntity/FreeSql.Extensions.ZeroEntity.csproj
@@ -23,7 +23,7 @@
-
+
@@ -39,5 +39,5 @@
-
+
diff --git a/Extensions/FreeSql.Extensions.ZeroEntity/Models/SchemaValidationException.cs b/Extensions/FreeSql.Extensions.ZeroEntity/Models/SchemaValidationException.cs
new file mode 100644
index 00000000..0c6f1fcd
--- /dev/null
+++ b/Extensions/FreeSql.Extensions.ZeroEntity/Models/SchemaValidationException.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace FreeSql.Extensions.ZeroEntity.Models
+{
+ public class SchemaValidationException : Exception
+ {
+ public SchemaValidationException(string message) : base(message) { }
+ }
+}
diff --git a/Extensions/FreeSql.Extensions.ZeroEntity/Models/SchemaValidationResult.cs b/Extensions/FreeSql.Extensions.ZeroEntity/Models/SchemaValidationResult.cs
new file mode 100644
index 00000000..efbaa185
--- /dev/null
+++ b/Extensions/FreeSql.Extensions.ZeroEntity/Models/SchemaValidationResult.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace FreeSql.Extensions.ZeroEntity.Models
+{
+ public class SchemaValidationResult
+ {
+ public readonly static SchemaValidationResult _schemaValidationResult = new SchemaValidationResult();
+
+ public static SchemaValidationResult SuccessedResult => _schemaValidationResult;
+
+ public SchemaValidationResult(string errorMessage)
+ {
+ ErrorMessage = errorMessage;
+ }
+
+ public string ErrorMessage { get; set; }
+ public bool Succeeded { get; set; } = false;
+ }
+}
diff --git a/Extensions/FreeSql.Extensions.ZeroEntity/ZeroDbContext.cs b/Extensions/FreeSql.Extensions.ZeroEntity/ZeroDbContext.cs
index b154a8d0..4f799e35 100644
--- a/Extensions/FreeSql.Extensions.ZeroEntity/ZeroDbContext.cs
+++ b/Extensions/FreeSql.Extensions.ZeroEntity/ZeroDbContext.cs
@@ -1,4 +1,5 @@
using FreeSql.DataAnnotations;
+using FreeSql.Extensions.ZeroEntity.Models;
using FreeSql.Internal;
using FreeSql.Internal.CommonProvider;
using FreeSql.Internal.Model;
@@ -14,7 +15,7 @@ using T = System.Collections.Generic.Dictionary;
namespace FreeSql.Extensions.ZeroEntity
{
- /*
+ /*
理解本机制之前,请先忘记 Repository/DbContext 等之前有关级联的内容,他们没有关联。
schemas[] 是一组表映射信息定义,包含表名、列名、导航属性、索引等信息
@@ -58,1426 +59,1462 @@ OneToMany 级联删除
ManyToOne 忽略
ManyToMany 级联删除中间表(注意不删除外部根)
*/
- public partial class ZeroDbContext
- {
- internal IFreeSql _orm;
- internal DbTransaction _transaction;
- internal int _commandTimeout;
- internal List _tables;
- public ZeroDbContext(IFreeSql orm, TableDescriptor[] schemas)
- {
- _orm = orm;
- _tables = ValidateSchemaToInfo(orm, schemas);
- if (orm.CodeFirst.IsAutoSyncStructure)
- foreach (var table in _tables)
- orm.CodeFirst.SyncStructure(table, table.DbName, false);
- }
+ public partial class ZeroDbContext
+ {
+ internal IFreeSql _orm;
+ internal DbTransaction _transaction;
+ internal int _commandTimeout;
+ internal List _tables;
- public TableInfo GetTableInfo(string name) => _tables.Where(a => a.CsName == name).FirstOrDefault();
- public void SyncStructure(string name)
- {
- var table = GetTableInfo(name);
- _orm.CodeFirst.SyncStructure(table, table.DbName, false);
- }
+ ///
+ /// 创建新的ZeroDbCotext实例
+ ///
+ /// IfreeSql 对象
+ /// 动态表结构描述
+ /// 是否强制同步表结构
+ /// Schema 未验证通过时抛出验证异常
+ public ZeroDbContext(IFreeSql orm, TableDescriptor[] schemas, bool syncStructure = false)
+ {
+ _orm = orm;
+ _tables = ValidateSchemaToInfoInternal(orm, schemas);
+ if (syncStructure || orm.CodeFirst.IsAutoSyncStructure)
+ {
+ foreach (var table in _tables)
+ orm.CodeFirst.SyncStructure(table, table.DbName, false);
+ }
+ }
- static List ValidateSchemaToInfo(IFreeSql orm, IEnumerable schemas)
- {
- var common = (orm.Ado as AdoProvider)._util;
- var tables = new List();
- foreach (var dtd in schemas)
- {
- if (string.IsNullOrWhiteSpace(dtd.Name)) continue;
- if (string.IsNullOrWhiteSpace(dtd.DbName)) dtd.DbName = dtd.Name;
- var tabattr = new TableAttribute
- {
- Name = dtd.DbName,
- AsTable = dtd.AsTable,
- };
- var tabindexs = dtd.Indexes.Select(a => new IndexAttribute(a.Name, a.Fields, a.IsUnique)
- {
- IndexMethod = a.IndexMethod,
- });
- var tab = new ZeroTableInfo();
- tab.Comment = dtd.Comment;
- tab.Type = typeof(object);
- tab.CsName = dtd.Name;
- tab.DbName = dtd.DbName;
- var isQuery = tab.DbName.StartsWith("(") && tab.DbName.EndsWith(")");
+ public SchemaValidationResult ValidateSchema(IEnumerable schemas)
+ {
+ try
+ {
+ ValidateSchemaToInfoInternal(_orm, schemas);
+ }
+ catch (SchemaValidationException ex)
+ {
+ return new SchemaValidationResult(ex.Message);
+ }
+ return SchemaValidationResult.SuccessedResult;
+ }
- if (isQuery == false)
- {
- if (orm.CodeFirst.IsSyncStructureToLower) tab.DbName = tab.DbName.ToLower();
- if (orm.CodeFirst.IsSyncStructureToUpper) tab.DbName = tab.DbName.ToUpper();
- }
- tab.DisableSyncStructure = isQuery || dtd.DisableSyncStructure;
- tab.IsDictionaryType = true;
- var columnsList = new List();
- foreach (var dtdcol in dtd.Columns)
- {
- if (string.IsNullOrWhiteSpace(dtdcol.Name) || dtdcol.MapType == null || tab.ColumnsByCs.ContainsKey(dtdcol.Name)) continue;
- var tp = common.CodeFirst.GetDbInfo(dtdcol.MapType);
- var colattr = dtdcol.ToAttribute();
- var col = Utils.ColumnAttributeToInfo(tab, null, colattr.Name, colattr.MapType, false, ref colattr, tp, common);
- if (col == null) continue;
+ public TableInfo GetTableInfo(string name) => _tables.Where(a => a.CsName == name).FirstOrDefault();
- col.Comment = dtdcol.Comment;
- tab.Columns.Add(col.Attribute.Name, col);
- tab.ColumnsByCs.Add(col.CsName, col);
- columnsList.Add(col);
- }
- Utils.AuditTableInfo(tab, tabattr, tabindexs, columnsList, common);
- columnsList.Clear();
- tables.Add(tab);
- }
- var tabindex = 0;
- foreach (var dtd in schemas)
- {
- var tab = tables[tabindex++];
- foreach (var dtdnav in dtd.Navigates)
- {
- if (tab.Navigates.ContainsKey(dtdnav.Name)) continue;
- var error = $"表“{tab.CsName}”导航属性 {dtdnav.Name} 配置错误:";
- var nav = new ZeroTableRef();
- nav.NavigateKey = dtdnav.Name;
- nav.Table = tab;
- nav.RefTable = tables.Where(a => a.CsName == dtdnav.RelTable).FirstOrDefault();
- if (nav.RefTable == null) throw new Exception($"{error}未定义“{dtdnav.RelTable}”");
+ public void SyncStructure()
+ {
+ foreach (var table in _tables)
+ _orm.CodeFirst.SyncStructure(table, table.DbName, false);
+ }
- switch (dtdnav.Type)
- {
- case TableDescriptor.NavigateType.OneToOne:
- nav.RefType = TableRefType.OneToOne;
- nav.Columns.AddRange(nav.Table.Primarys.Select(a => a.CsName));
- if (string.IsNullOrWhiteSpace(dtdnav.Bind))
- nav.RefColumns.AddRange(nav.RefTable.Primarys.Select(a => a.CsName));
- else
- nav.RefColumns.AddRange(dtdnav.Bind.Split(',')
- .Select(a => nav.RefTable.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
- .Where(a => string.IsNullOrWhiteSpace(a) == false));
- break;
- case TableDescriptor.NavigateType.ManyToOne:
- nav.RefType = TableRefType.ManyToOne;
- nav.Columns.AddRange(dtdnav.Bind.Split(',')
- .Select(a => nav.Table.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
- .Where(a => string.IsNullOrWhiteSpace(a) == false));
- nav.RefColumns.AddRange(nav.RefTable.Primarys.Select(a => a.CsName));
- break;
- case TableDescriptor.NavigateType.OneToMany:
- nav.RefType = TableRefType.OneToMany;
- nav.Columns.AddRange(nav.Table.Primarys.Select(a => a.CsName));
- nav.RefColumns.AddRange(dtdnav.Bind.Split(',')
- .Select(a => nav.RefTable.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
- .Where(a => string.IsNullOrWhiteSpace(a) == false));
- break;
- case TableDescriptor.NavigateType.ManyToMany:
- nav.RefType = TableRefType.ManyToMany;
- var midtab = tables.Where(a => a.CsName == dtdnav.ManyToMany).FirstOrDefault();
- nav.RefMiddleTable = midtab;
- if (nav.RefMiddleTable == null) throw new Exception($"{error}ManyToMany未定义“{dtdnav.ManyToMany}”");
- var midtabRaw = schemas.Where(a => a.Name == midtab.CsName).FirstOrDefault();
- var midTabNav1 = midtabRaw.Navigates.Where(a => a.Type == TableDescriptor.NavigateType.ManyToOne && a.RelTable == nav.Table.CsName).FirstOrDefault();
- if (midTabNav1 == null) throw new Exception($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”没有与表“{nav.Table.CsName}”形成 ManyToOne 关联");
- var midTabNav1Columns = midTabNav1.Bind.Split(',')
- .Select(a => midtab.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
- .Where(a => string.IsNullOrWhiteSpace(a) == false).ToArray();
- var midTabNav2 = midtabRaw.Navigates.Where(a => a.Type == TableDescriptor.NavigateType.ManyToOne && a.RelTable == nav.RefTable.CsName).FirstOrDefault();
- if (midTabNav2 == null) throw new Exception($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”没有与表“{nav.RefTable.CsName}”形成 ManyToOne 关联");
- var midTabNav2Columns = midTabNav2.Bind.Split(',')
- .Select(a => midtab.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
- .Where(a => string.IsNullOrWhiteSpace(a) == false).ToArray();
- if (midTabNav1Columns.Length != nav.Table.Primarys.Length) throw new Exception($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”关联字段的数目不相等");
- if (midTabNav1Columns.Where((a, idx) => midtab.ColumnsByCs[a].CsType != nav.Table.Primarys[idx].CsType).Any()) throw new Exception($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”关联字段的类型不相等");
- if (midTabNav2Columns.Length != nav.RefTable.Primarys.Length) throw new Exception($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”与表“{nav.RefTable.CsName}”关联字段的数目不相等");
- if (midTabNav2Columns.Where((a, idx) => midtab.ColumnsByCs[a].CsType != nav.RefTable.Primarys[idx].CsType).Any()) throw new Exception($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”与表“{nav.RefTable.CsName}”关联字段的类型不相等");
- nav.Columns.AddRange(nav.Table.Primarys.Select(a => a.CsName));
- nav.MiddleColumns.AddRange(midTabNav1Columns);
- nav.MiddleColumns.AddRange(midTabNav2Columns);
- nav.RefColumns.AddRange(nav.RefTable.Primarys.Select(a => a.CsName));
- break;
- }
- switch (dtdnav.Type)
- {
- case TableDescriptor.NavigateType.OneToOne:
- case TableDescriptor.NavigateType.ManyToOne:
- case TableDescriptor.NavigateType.OneToMany:
- if (nav.Columns.Any() == false || nav.Columns.Count != nav.RefColumns.Count) throw new Exception($"{error}与表“{dtdnav.RelTable}”关联字段的数目不相等");
- if (nav.Columns.Where((a, idx) => nav.Table.ColumnsByCs[a].CsType != nav.RefTable.ColumnsByCs[nav.RefColumns[idx]].CsType).Any()) throw new Exception($"{error}与表“{dtdnav.RelTable}”关联字段的类型不匹配");
- break;
- }
- tab.Navigates.Add(dtdnav.Name, nav);
- }
- }
- return tables;
- }
+ ///
+ /// 同步指定表结构
+ ///
+ ///
+ public void SyncTableStructure(string name)
+ {
+ var table = GetTableInfo(name);
+ _orm.CodeFirst.SyncStructure(table, table.DbName, false);
+ }
- public ZeroDbContext WithTransaction(DbTransaction value)
- {
- _transaction = value;
- return this;
- }
- public ZeroDbContext CommandTimeout(int seconds)
- {
- _commandTimeout = seconds;
- return this;
- }
- void TransactionInvoke(Action handler)
- {
- if (_transaction == null)
- {
- var threadTransaction = _orm.Ado.TransactionCurrentThread;
- if (threadTransaction != null) this.WithTransaction(threadTransaction);
- }
- if (_transaction != null)
- {
- handler?.Invoke();
- return;
- }
- using (var conn = _orm.Ado.MasterPool.Get())
- {
- _transaction = conn.Value.BeginTransaction();
- var transBefore = new Aop.TraceBeforeEventArgs("BeginTransaction", null);
- try
- {
- _orm.Aop.TraceBeforeHandler?.Invoke(this, transBefore);
- handler?.Invoke();
- _transaction.Commit();
- _orm.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(transBefore, CoreStrings.Commit, null));
- }
- catch (Exception ex)
- {
- _transaction.Rollback();
- _orm.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(transBefore, CoreStrings.RollBack, ex));
- throw;
- }
- finally
- {
- _transaction = null;
- }
- }
- }
- ///
- /// 【有状态管理】自动 Include 查询
- ///
- public SelectImpl Select => new SelectImpl(this, _tables[0].CsName).IncludeAll();
- ///
- /// 【无状态管理】指定表查询
- ///
- public SelectImpl SelectNoTracking(string tableName) => new SelectImpl(this, tableName).NoTracking();
- public int Insert(T entity) => Insert(new[] { entity });
- public int Insert(IEnumerable entities)
- {
- _cascadeAffrows = 0;
- _cascadeIgnores.Clear();
- try
- {
- TransactionInvoke(() =>
- {
- AuditCascade(_tables[0], entities);
- InsertCascade(_tables[0], entities, true);
- });
- return _cascadeAffrows;
- }
- finally
- {
- _cascadeAffrows = 0;
- _cascadeIgnores.Clear();
- }
- }
- public int Update(T entity) => Update(new[] { entity });
- public int Update(IEnumerable entities)
- {
- _cascadeAffrows = 0;
- _cascadeIgnores.Clear();
- try
- {
- TransactionInvoke(() =>
- {
- AuditCascade(_tables[0], entities);
- UpdateCascade(_tables[0], entities, true);
- });
- return _cascadeAffrows;
- }
- finally
- {
- _cascadeAffrows = 0;
- _cascadeIgnores.Clear();
- }
- }
- public int Delete(T entity) => Delete(new[] { entity });
- public int Delete(IEnumerable entities)
- {
- _cascadeAffrows = 0;
- _cascadeIgnores.Clear();
- try
- {
- TransactionInvoke(() =>
- {
- AuditCascade(_tables[0], entities);
- DeleteCascade(_tables[0], entities, null);
- });
- return _cascadeAffrows;
- }
- finally
- {
- _cascadeAffrows = 0;
- _cascadeIgnores.Clear();
- }
- }
- public void FlushState()
- {
- _states.Clear();
- }
- public void Attach(T entity)
- {
- AuditCascade(_tables[0], entity);
- AttachCascade(_tables[0], entity, true);
- }
+ static List ValidateSchemaToInfoInternal(IFreeSql orm, IEnumerable schemas)
+ {
+ var common = (orm.Ado as AdoProvider)._util;
+ var tables = new List();
+ foreach (var dtd in schemas)
+ {
+ if (string.IsNullOrWhiteSpace(dtd.Name)) continue;
+ if (string.IsNullOrWhiteSpace(dtd.DbName)) dtd.DbName = dtd.Name;
+ var tabattr = new TableAttribute
+ {
+ Name = dtd.DbName,
+ AsTable = dtd.AsTable,
+ };
+ var tabindexs = dtd.Indexes.Select(a => new IndexAttribute(a.Name, a.Fields, a.IsUnique)
+ {
+ IndexMethod = a.IndexMethod,
+ });
+ var tab = new ZeroTableInfo();
+ tab.Comment = dtd.Comment;
+ tab.Type = typeof(object);
+ tab.CsName = dtd.Name;
+ tab.DbName = dtd.DbName;
+ var isQuery = tab.DbName.StartsWith("(") && tab.DbName.EndsWith(")");
- void AuditCascade(ZeroTableInfo entityTable, IEnumerable entities)
- {
- if (entities == null) return;
- foreach (var entity in entities) AuditCascade(entityTable, entity);
- }
- internal void AuditCascade(ZeroTableInfo entityTable, T entity)
- {
- var ignores = new Dictionary>(); //比如 Tree 结构可以递归添加
- LocalAuditCascade(entityTable, entity);
- ignores.Clear();
+ if (isQuery == false)
+ {
+ if (orm.CodeFirst.IsSyncStructureToLower) tab.DbName = tab.DbName.ToLower();
+ if (orm.CodeFirst.IsSyncStructureToUpper) tab.DbName = tab.DbName.ToUpper();
+ }
+ tab.DisableSyncStructure = isQuery || dtd.DisableSyncStructure;
+ tab.IsDictionaryType = true;
+ var columnsList = new List();
+ foreach (var dtdcol in dtd.Columns)
+ {
+ if (string.IsNullOrWhiteSpace(dtdcol.Name) || dtdcol.MapType == null || tab.ColumnsByCs.ContainsKey(dtdcol.Name)) continue;
+ var tp = common.CodeFirst.GetDbInfo(dtdcol.MapType);
+ var colattr = dtdcol.ToAttribute();
+ var col = Utils.ColumnAttributeToInfo(tab, null, colattr.Name, colattr.MapType, false, ref colattr, tp, common);
+ if (col == null) continue;
- void LocalAuditCascade(ZeroTableInfo table, T entityFrom)
- {
- if (entityFrom == null) return;
+ col.Comment = dtdcol.Comment;
+ tab.Columns.Add(col.Attribute.Name, col);
+ tab.ColumnsByCs.Add(col.CsName, col);
+ columnsList.Add(col);
+ }
+ Utils.AuditTableInfo(tab, tabattr, tabindexs, columnsList, common);
+ columnsList.Clear();
+ tables.Add(tab);
+ }
+ var tabindex = 0;
+ foreach (var dtd in schemas)
+ {
+ var tab = tables[tabindex++];
+ foreach (var dtdnav in dtd.Navigates)
+ {
+ if (tab.Navigates.ContainsKey(dtdnav.Name)) continue;
+ var error = $"表“{tab.CsName}”导航属性 {dtdnav.Name} 配置错误:";
+ var nav = new ZeroTableRef();
+ nav.NavigateKey = dtdnav.Name;
+ nav.Table = tab;
+ nav.RefTable = tables.Where(a => a.CsName == dtdnav.RelTable).FirstOrDefault();
- var stateKey = GetEntityKeyString(table, entityFrom);
- if (ignores.TryGetValue(table.DbName, out var stateKeys) == false) ignores.Add(table.DbName, stateKeys = new Dictionary());
- if (stateKey != null)
- {
- if (stateKeys.ContainsKey(stateKey)) return;
- stateKeys.Add(stateKey, true);
- }
+ if (nav.RefTable == null) throw new SchemaValidationException($"{error}未定义“{dtdnav.RelTable}”");
- foreach (var col in table.Columns.Values)
- if (entityFrom.TryGetValue(col.CsName, out var colval))
- entityFrom[col.CsName] = Utils.GetDataReaderValue(col.CsType, colval);
+ switch (dtdnav.Type)
+ {
+ case TableDescriptor.NavigateType.OneToOne:
+ nav.RefType = TableRefType.OneToOne;
+ nav.Columns.AddRange(nav.Table.Primarys.Select(a => a.CsName));
+ if (string.IsNullOrWhiteSpace(dtdnav.Bind))
+ nav.RefColumns.AddRange(nav.RefTable.Primarys.Select(a => a.CsName));
+ else
+ nav.RefColumns.AddRange(dtdnav.Bind.Split(',')
+ .Select(a => nav.RefTable.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
+ .Where(a => string.IsNullOrWhiteSpace(a) == false));
+ break;
+ case TableDescriptor.NavigateType.ManyToOne:
+ nav.RefType = TableRefType.ManyToOne;
+ nav.Columns.AddRange(dtdnav.Bind.Split(',')
+ .Select(a => nav.Table.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
+ .Where(a => string.IsNullOrWhiteSpace(a) == false));
+ nav.RefColumns.AddRange(nav.RefTable.Primarys.Select(a => a.CsName));
+ break;
+ case TableDescriptor.NavigateType.OneToMany:
+ nav.RefType = TableRefType.OneToMany;
+ nav.Columns.AddRange(nav.Table.Primarys.Select(a => a.CsName));
+ nav.RefColumns.AddRange(dtdnav.Bind.Split(',')
+ .Select(a => nav.RefTable.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
+ .Where(a => string.IsNullOrWhiteSpace(a) == false));
+ break;
+ case TableDescriptor.NavigateType.ManyToMany:
+ nav.RefType = TableRefType.ManyToMany;
+ var midtab = tables.Where(a => a.CsName == dtdnav.ManyToMany).FirstOrDefault();
+ nav.RefMiddleTable = midtab;
+ if (nav.RefMiddleTable == null) throw new SchemaValidationException($"{error}ManyToMany未定义“{dtdnav.ManyToMany}”");
+ var midtabRaw = schemas.Where(a => a.Name == midtab.CsName).FirstOrDefault();
+ var midTabNav1 = midtabRaw.Navigates.Where(a => a.Type == TableDescriptor.NavigateType.ManyToOne && a.RelTable == nav.Table.CsName).FirstOrDefault();
+ if (midTabNav1 == null) throw new SchemaValidationException($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”没有与表“{nav.Table.CsName}”形成 ManyToOne 关联");
+ var midTabNav1Columns = midTabNav1.Bind.Split(',')
+ .Select(a => midtab.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
+ .Where(a => string.IsNullOrWhiteSpace(a) == false).ToArray();
+ var midTabNav2 = midtabRaw.Navigates.Where(a => a.Type == TableDescriptor.NavigateType.ManyToOne && a.RelTable == nav.RefTable.CsName).FirstOrDefault();
+ if (midTabNav2 == null) throw new SchemaValidationException($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”没有与表“{nav.RefTable.CsName}”形成 ManyToOne 关联");
+ var midTabNav2Columns = midTabNav2.Bind.Split(',')
+ .Select(a => midtab.ColumnsByCs.TryGetValue(a.Trim(), out var refcol) ? refcol.CsName : "")
+ .Where(a => string.IsNullOrWhiteSpace(a) == false).ToArray();
+ if (midTabNav1Columns.Length != nav.Table.Primarys.Length) throw new SchemaValidationException($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”关联字段的数目不相等");
+ if (midTabNav1Columns.Where((a, idx) => midtab.ColumnsByCs[a].CsType != nav.Table.Primarys[idx].CsType).Any()) throw new SchemaValidationException($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”关联字段的类型不相等");
+ if (midTabNav2Columns.Length != nav.RefTable.Primarys.Length) throw new SchemaValidationException($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”与表“{nav.RefTable.CsName}”关联字段的数目不相等");
+ if (midTabNav2Columns.Where((a, idx) => midtab.ColumnsByCs[a].CsType != nav.RefTable.Primarys[idx].CsType).Any()) throw new SchemaValidationException($"{error}ManyToMany中间表“{dtdnav.ManyToMany}”与表“{nav.RefTable.CsName}”关联字段的类型不相等");
+ nav.Columns.AddRange(nav.Table.Primarys.Select(a => a.CsName));
+ nav.MiddleColumns.AddRange(midTabNav1Columns);
+ nav.MiddleColumns.AddRange(midTabNav2Columns);
+ nav.RefColumns.AddRange(nav.RefTable.Primarys.Select(a => a.CsName));
+ break;
+ }
+ switch (dtdnav.Type)
+ {
+ case TableDescriptor.NavigateType.OneToOne:
+ case TableDescriptor.NavigateType.ManyToOne:
+ case TableDescriptor.NavigateType.OneToMany:
+ if (nav.Columns.Any() == false || nav.Columns.Count != nav.RefColumns.Count) throw new SchemaValidationException($"{error}与表“{dtdnav.RelTable}”关联字段的数目不相等");
+ if (nav.Columns.Where((a, idx) => nav.Table.ColumnsByCs[a].CsType != nav.RefTable.ColumnsByCs[nav.RefColumns[idx]].CsType).Any()) throw new SchemaValidationException($"{error}与表“{dtdnav.RelTable}”关联字段的类型不匹配");
+ break;
+ }
+ tab.Navigates.Add(dtdnav.Name, nav);
+ }
+ }
+ return tables;
+ }
- foreach (var nav in table.Navigates)
- {
- if (entityFrom.TryGetValue(nav.Key, out var propvalFrom) == false || propvalFrom == null) continue;
- switch (nav.Value.RefType)
- {
- case TableRefType.OneToOne:
- {
- if (propvalFrom is T valFrom == false) valFrom = LocalAuditJsonElement(nv => entityFrom[nav.Key] = nv, propvalFrom) as T;
- if (valFrom == null) continue;
- SetNavigateRelationshipValue(nav.Value, entityFrom, valFrom);
- LocalAuditCascade(nav.Value.RefTable, valFrom);
- }
- break;
- case TableRefType.OneToMany:
- {
- if (propvalFrom is IEnumerable valFromList == false) valFromList = LocalAuditJsonElement(nv => entityFrom[nav.Key] = nv, propvalFrom) as IEnumerable;
- else if (valFromList != null)
- {
- foreach (var fromObj in valFromList)
- {
- if (fromObj is T == false) valFromList = LocalAuditJsonElement(nv => entityFrom[nav.Key] = nv, propvalFrom) as IEnumerable;
- break;
- }
- }
- if (valFromList == null) continue;
- SetNavigateRelationshipValue(nav.Value, entityFrom, valFromList);
- foreach (var fromObj in valFromList)
- {
- if (fromObj is T fromItem == false || fromItem == null) continue;
- LocalAuditCascade(nav.Value.RefTable, fromItem);
- }
- }
- break;
- case TableRefType.ManyToMany:
- {
- if (propvalFrom is IEnumerable valFromList == false) valFromList = LocalAuditJsonElement(nv => entityFrom[nav.Key] = nv, propvalFrom) as IEnumerable;
- else if (valFromList != null)
- {
- foreach (var fromObj in valFromList)
- {
- if (fromObj is T == false) valFromList = LocalAuditJsonElement(nv => entityFrom[nav.Key] = nv, propvalFrom) as IEnumerable;
- break;
- }
- }
- if (valFromList == null) continue;
- foreach (var fromObj in valFromList)
- {
- if (fromObj is T fromItem == false || fromItem == null) continue;
- LocalAuditCascade(nav.Value.RefTable, fromItem);
- }
- }
- break;
- case TableRefType.ManyToOne:
- {
- if (propvalFrom is T valFrom == false) valFrom = LocalAuditJsonElement(nv => entityFrom[nav.Key] = nv, propvalFrom) as T;
- if (valFrom == null) continue;
- LocalAuditCascade(nav.Value.RefTable, valFrom);
- }
- break;
- }
- }
- }
+ public ZeroDbContext WithTransaction(DbTransaction value)
+ {
+ _transaction = value;
+ return this;
+ }
+ public ZeroDbContext CommandTimeout(int seconds)
+ {
+ _commandTimeout = seconds;
+ return this;
+ }
+ void TransactionInvoke(Action handler)
+ {
+ if (_transaction == null)
+ {
+ var threadTransaction = _orm.Ado.TransactionCurrentThread;
+ if (threadTransaction != null) this.WithTransaction(threadTransaction);
+ }
+ if (_transaction != null)
+ {
+ handler?.Invoke();
+ return;
+ }
+ using (var conn = _orm.Ado.MasterPool.Get())
+ {
+ _transaction = conn.Value.BeginTransaction();
+ var transBefore = new Aop.TraceBeforeEventArgs("BeginTransaction", null);
+ try
+ {
+ _orm.Aop.TraceBeforeHandler?.Invoke(this, transBefore);
+ handler?.Invoke();
+ _transaction.Commit();
+ _orm.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(transBefore, CoreStrings.Commit, null));
+ }
+ catch (Exception ex)
+ {
+ _transaction.Rollback();
+ _orm.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(transBefore, CoreStrings.RollBack, ex));
+ throw;
+ }
+ finally
+ {
+ _transaction = null;
+ }
+ }
+ }
+ ///
+ /// 【有状态管理】自动 Include 查询
+ ///
+ public SelectImpl Select => new SelectImpl(this, _tables[0].CsName).IncludeAll();
+ ///
+ /// 【无状态管理】指定表查询
+ ///
+ public SelectImpl SelectNoTracking(string tableName) => new SelectImpl(this, tableName).NoTracking();
- object LocalAuditJsonElement(Func