From 8248057d5cd6ca60b4770b960f985d3da0b75cfd Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 4 Jun 2022 00:35:33 +0800 Subject: [PATCH 01/20] init ArrayToMany --- FreeSql.DbContext/DbSet/DbSetAsync.cs | 16 +- FreeSql.DbContext/DbSet/DbSetSync.cs | 14 +- FreeSql/Internal/CommonExpression.cs | 5 + .../SelectProvider/Select1Provider.cs | 4 + FreeSql/Internal/Model/TableInfo.cs | 2 +- FreeSql/Internal/UtilsExpressionTree.cs | 265 ++++++++++++------ 6 files changed, 212 insertions(+), 94 deletions(-) diff --git a/FreeSql.DbContext/DbSet/DbSetAsync.cs b/FreeSql.DbContext/DbSet/DbSetAsync.cs index 13533430e..5bdccd0cf 100644 --- a/FreeSql.DbContext/DbSet/DbSetAsync.cs +++ b/FreeSql.DbContext/DbSet/DbSetAsync.cs @@ -155,8 +155,9 @@ async public Task SaveManyAsync(TEntity item, string propertyName, CancellationT if (tref == null) return; switch (tref.RefType) { - case Internal.Model.TableRefType.OneToOne: - case Internal.Model.TableRefType.ManyToOne: + case TableRefType.OneToOne: + case TableRefType.ManyToOne: + case TableRefType.ArrayToMany: throw new ArgumentException(DbContextStrings.PropertyOfType_IsNot_OneToManyOrManyToMany(_table.Type.FullName, propertyName)); } @@ -166,7 +167,7 @@ async public Task SaveManyAsync(TEntity item, string propertyName, CancellationT try { await AddOrUpdateNavigateAsync(item, false, propertyName, cancellationToken); - if (tref.RefType == Internal.Model.TableRefType.OneToMany) + if (tref.RefType == TableRefType.OneToMany) { await DbContextFlushCommandAsync(cancellationToken); //删除没有保存的数据,求出主体的条件 @@ -213,7 +214,7 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam DbSet refSet = null; switch (tref.RefType) { - case Internal.Model.TableRefType.OneToOne: + case TableRefType.OneToOne: refSet = GetDbSetObject(tref.RefEntityType); var propValItem = GetItemValue(item, prop); if (propValItem == null) return; @@ -225,7 +226,8 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam if (isAdd) await refSet.AddAsync(propValItem); else await refSet.AddOrUpdateAsync(propValItem); return; - case Internal.Model.TableRefType.ManyToOne: + case TableRefType.ManyToOne: + case TableRefType.ArrayToMany: return; } @@ -234,7 +236,7 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam refSet = GetDbSetObject(tref.RefEntityType); switch (tref.RefType) { - case Internal.Model.TableRefType.ManyToMany: + case TableRefType.ManyToMany: var curList = new List(); foreach (var propValItem in propValEach) { @@ -327,7 +329,7 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam await midSet.AddRangeAsync(midListAdd, cancellationToken); } break; - case Internal.Model.TableRefType.OneToMany: + case TableRefType.OneToMany: var addList = new List(); var addOrUpdateList = new List(); foreach (var propValItem in propValEach) diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index 8bafd8ad1..fc9bb1e10 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -166,8 +166,9 @@ public void SaveMany(TEntity item, string propertyName) if (tref == null) return; switch (tref.RefType) { - case Internal.Model.TableRefType.OneToOne: - case Internal.Model.TableRefType.ManyToOne: + case TableRefType.OneToOne: + case TableRefType.ManyToOne: + case TableRefType.ArrayToMany: throw new ArgumentException(DbContextStrings.PropertyOfType_IsNot_OneToManyOrManyToMany(_table.Type.FullName, propertyName)); } @@ -177,7 +178,7 @@ public void SaveMany(TEntity item, string propertyName) try { AddOrUpdateNavigate(item, false, propertyName); - if (tref.RefType == Internal.Model.TableRefType.OneToMany) + if (tref.RefType == TableRefType.OneToMany) { DbContextFlushCommand(); //删除没有保存的数据,求出主体的条件 @@ -224,7 +225,7 @@ void AddOrUpdateNavigate(TEntity item, bool isAdd, string propertyName) DbSet refSet = null; switch (tref.RefType) { - case Internal.Model.TableRefType.OneToOne: + case TableRefType.OneToOne: refSet = GetDbSetObject(tref.RefEntityType); var propValItem = GetItemValue(item, prop); if (propValItem == null) return; @@ -236,7 +237,8 @@ void AddOrUpdateNavigate(TEntity item, bool isAdd, string propertyName) if (isAdd) refSet.Add(propValItem); else refSet.AddOrUpdate(propValItem); return; - case Internal.Model.TableRefType.ManyToOne: + case TableRefType.ManyToOne: + case TableRefType.ArrayToMany: return; } @@ -714,7 +716,7 @@ List> LocalGetNavigates(TableInfo tb) { return tb.Properties.Where(a => tb.ColumnsByCs.ContainsKey(a.Key) == false) .Select(a => new NativeTuple(tb.GetTableRef(a.Key, false), a.Value)) - .Where(a => a.Item1 != null && a.Item1.RefType != TableRefType.ManyToOne) + .Where(a => a.Item1 != null && new[] { TableRefType.OneToOne, TableRefType.OneToMany, TableRefType.ManyToMany }.Contains(a.Item1.RefType)) .ToList(); } void LocalEach(DbSet dbset, IEnumerable items, bool isOneToOne) diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index c806edd36..eefbd8530 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -154,6 +154,7 @@ public bool ReadAnonymousField(List _tables, StringBuilder fiel { case TableRefType.ManyToMany: case TableRefType.OneToMany: + case TableRefType.ArrayToMany: continue; } if (_tables.Any(a => a.Alias == $"{map.First().Table.Alias}__{memProp.Name}") == false) continue; @@ -2194,6 +2195,8 @@ void LocalSetSelectProviderAlias(string alias) for (var tidx = 0; tidx < memberTbref.Columns.Count; tidx++) select.Where($"{select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[tidx].Attribute.Name)} = {omtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[tidx].Attribute.Name)}"); break; + case TableRefType.ArrayToMany: + break; } } @@ -2241,6 +2244,8 @@ void LocalInitSelectProvider() if (select != null) return; LocalInitSelectProvider(); continue; + case TableRefType.ArrayToMany: + continue; } } if (exp4.NodeType == ExpressionType.Call) diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index e28b43d1f..36cde5e9d 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -430,6 +430,8 @@ public ISelect IncludeByPropertyName(string property) var curTb = _commonUtils.GetTableByEntity(exp.Type); _commonExpression.ExpressionWhereLambda(_tables, Expression.MakeMemberAccess(exp, curTb.Properties[curTb.ColumnsByCs.First().Value.CsName]), null, null, null); break; + case TableRefType.ArrayToMany: + break; } return this; } @@ -1172,6 +1174,8 @@ public ISelect IncludeMany(Expression a.FullName.EndsWith(trytbTypeLazyName)).FirstOrDefault(); trytb.TypeLazy = type; @@ -989,111 +989,215 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo } } else - { //One To Many - List bindColumns = new List(); - if (pnvBind != null) + { + var isArrayToMany = false; + var lmbdWhere = isLazy ? new StringBuilder() : null; + //Pgsql Array[] To Many + if (common._orm.Ado.DataType == DataType.PostgreSQL) { - foreach (var bi in pnvBind) + //class User { + // public int[] RoleIds { get; set; } + // [Navigate(nameof(RoleIds))] + // public Role[] Roles { get; set; } + //} + //class Role { + // [Navigate(nameof(User.RoleIds))] + // public User[] Users { get; set; } + //} + ColumnInfo trycol = null; + if (tbref.Primarys.Length == 1) { - if (tbref.ColumnsByCs.TryGetValue(bi, out var trybindcol) == false) + if (pnvBind?.Length == 1) + { + if (trytb.ColumnsByCs.TryGetValue(pnvBind[0], out trycol)) + { + if (trycol.CsType.IsArray == true && tbref.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + { + nvref.Exception = new Exception($"导航属性 {trytbTypeName}.{pnv.Name} 特性 [Navigate] 解析错误,{trytbTypeName}.{trycol.CsName} 数组元素 与 {tbrefTypeName}.{tbref.Primarys[0].CsName} 类型不符"); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + } + } + } + if (nvref.Exception == null && trycol == null) + { + var findtbrefPkCsName = tbref.Primarys[0].CsName.TrimStart('_'); + if (findtbrefPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtbrefPkCsName = findtbrefPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); + var findtrytb = pnv.Name; + if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); + findtrytb += tbref.CsName; + if ( + tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtbrefPkCsName}", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtbrefPkCsName}", out trycol) == false //下划线命名 + ) + { + } + if (trycol != null && tbref.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + trycol = null; + } + isArrayToMany = trycol != null; + if (isArrayToMany) { - nvref.Exception = new Exception(CoreStrings.Navigation_ParsingError_NotFound_Property(trytbTypeName, pnv.Name, tbrefTypeName, bi)); + lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName).Append(")"); + nvref.Columns.Add(trycol); + nvref.RefColumns.Add(tbref.Primarys[0]); + nvref.RefEntityType = tbref.Type; + nvref.RefType = TableRefType.ArrayToMany; trytb.AddOrUpdateTableRef(pnv.Name, nvref); - //if (isLazy) throw nvref.Exception; - break; } - bindColumns.Add(trybindcol); - } - } - - PropertyInfo refprop = null; - var refcols = tbref.Properties.Where(z => z.Value.PropertyType == trytb.Type); - refprop = refcols.Count() == 1 ? refcols.First().Value : null; - var lmbdWhere = isLazy ? new StringBuilder() : null; - - if (nvref.Exception == null && bindColumns.Any() && bindColumns.Count != trytb.Primarys.Length) - { - nvref.Exception = new Exception(CoreStrings.Navigation_Bind_Number_Different(trytbTypeName, pnv.Name, bindColumns.Count, trytb.Primarys.Length)); - trytb.AddOrUpdateTableRef(pnv.Name, nvref); - //if (isLazy) throw nvref.Exception; - } - if (trytb.Primarys.Length > 1) - { - if (trytb.Primarys.Select(a => a.CsType.NullableTypeOrThis()).Distinct().Count() == trytb.Primarys.Length) - { - var pkList = trytb.Primarys.ToList(); - bindColumns.Sort((a, b) => pkList.FindIndex(c => c.CsType.NullableTypeOrThis() == a.CsType.NullableTypeOrThis()).CompareTo(pkList.FindIndex(c => c.CsType.NullableTypeOrThis() == b.CsType.NullableTypeOrThis()))); } - else if (string.Compare(string.Join(",", trytb.Primarys.Select(a => a.CsName).OrderBy(a => a)), string.Join(",", bindColumns.Select(a => a.CsName).OrderBy(a => a)), true) == 0) + + if (nvref.Exception == null && trytb.Primarys.Length == 1 && isArrayToMany == false) { - var pkList = trytb.Primarys.ToList(); - bindColumns.Sort((a, b) => pkList.FindIndex(c => string.Compare(c.CsName, a.CsName, true) == 0).CompareTo(pkList.FindIndex(c => string.Compare(c.CsName, b.CsName, true) == 0))); + if (pnvBind?.Length == 1) + { + if (tbref.ColumnsByCs.TryGetValue(pnvBind[0], out trycol)) + { + if (trycol.CsType.IsArray == true && trytb.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + { + nvref.Exception = new Exception($"导航属性 {trytbTypeName}.{pnv.Name} 特性 [Navigate] 解析错误,{trytbTypeName}.{trytb.Primarys[0].CsName} 与 {tbrefTypeName}.{trycol.CsName} 数组元素类型不符"); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + } + } + } + if (nvref.Exception == null && trycol == null) + { + var findtrytbPkCsName = trytb.Primarys[0].CsName.TrimStart('_'); + if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); + var findtrytb = pnv.Name; + if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); + findtrytb += trytb.CsName; + if ( + tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}", out trycol) == false //下划线命名 + ) + { + } + if (trycol != null && trytb.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + trycol = null; + } + isArrayToMany = trycol != null; + if (isArrayToMany) + { + lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName).Append(")"); + nvref.Columns.Add(tbref.Primarys[0]); + nvref.RefColumns.Add(trycol); + nvref.RefEntityType = tbref.Type; + nvref.RefType = TableRefType.ArrayToMany; + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + } } + } - for (var a = 0; nvref.Exception == null && a < trytb.Primarys.Length; a++) + + PropertyInfo refprop = null; + if (isArrayToMany == false) { - var findtrytbPkCsName = trytb.Primarys[a].CsName.TrimStart('_'); - if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); - var findtrytb = pnv.Name; - if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); - findtrytb += trytb.CsName; - - var trycol = bindColumns.Any() ? bindColumns[a] : null; - if (trycol == null && - tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 - tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}", out trycol) == false //下划线命名 - ) + List bindColumns = new List(); + //One To Many + if (pnvBind != null) { - if (refprop != null && - tbref.ColumnsByCs.TryGetValue($"{refprop.Name}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 - tbref.ColumnsByCs.TryGetValue($"{refprop.Name}_{findtrytbPkCsName}", out trycol) == false) //下划线命名 + foreach (var bi in pnvBind) { - + if (tbref.ColumnsByCs.TryGetValue(bi, out var trybindcol) == false) + { + nvref.Exception = new Exception(CoreStrings.Navigation_ParsingError_NotFound_Property(trytbTypeName, pnv.Name, tbrefTypeName, bi)); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + break; + } + bindColumns.Add(trybindcol); } } - if (trycol != null && trycol.CsType.NullableTypeOrThis() != trytb.Primarys[a].CsType.NullableTypeOrThis()) + + var refcols = tbref.Properties.Where(z => z.Value.PropertyType == trytb.Type); + refprop = refcols.Count() == 1 ? refcols.First().Value : null; + + if (nvref.Exception == null && bindColumns.Any() && bindColumns.Count != trytb.Primarys.Length) { - nvref.Exception = new Exception(CoreStrings.OneToMany_ParsingError_InconsistentType(trytbTypeName, pnv.Name, trytb.CsName, trytb.Primarys[a].CsName, tbref.CsName, trycol.CsName)); + nvref.Exception = new Exception(CoreStrings.Navigation_Bind_Number_Different(trytbTypeName, pnv.Name, bindColumns.Count, trytb.Primarys.Length)); trytb.AddOrUpdateTableRef(pnv.Name, nvref); //if (isLazy) throw nvref.Exception; - break; } - if (trycol == null) + if (trytb.Primarys.Length > 1) { - nvref.Exception = new Exception(CoreStrings.OneToMany_NotFound_CorrespondingField(trytbTypeName, pnv.Name, tbref.CsName, findtrytb, findtrytbPkCsName) - + (refprop == null ? "" : CoreStrings.OneToMany_UseNavigate(refprop.Name, findtrytbPkCsName))); - trytb.AddOrUpdateTableRef(pnv.Name, nvref); - //if (isLazy) throw nvref.Exception; - break; + if (trytb.Primarys.Select(a => a.CsType.NullableTypeOrThis()).Distinct().Count() == trytb.Primarys.Length) + { + var pkList = trytb.Primarys.ToList(); + bindColumns.Sort((a, b) => pkList.FindIndex(c => c.CsType.NullableTypeOrThis() == a.CsType.NullableTypeOrThis()).CompareTo(pkList.FindIndex(c => c.CsType.NullableTypeOrThis() == b.CsType.NullableTypeOrThis()))); + } + else if (string.Compare(string.Join(",", trytb.Primarys.Select(a => a.CsName).OrderBy(a => a)), string.Join(",", bindColumns.Select(a => a.CsName).OrderBy(a => a)), true) == 0) + { + var pkList = trytb.Primarys.ToList(); + bindColumns.Sort((a, b) => pkList.FindIndex(c => string.Compare(c.CsName, a.CsName, true) == 0).CompareTo(pkList.FindIndex(c => string.Compare(c.CsName, b.CsName, true) == 0))); + } } + for (var a = 0; nvref.Exception == null && a < trytb.Primarys.Length; a++) + { + var findtrytbPkCsName = trytb.Primarys[a].CsName.TrimStart('_'); + if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); + var findtrytb = pnv.Name; + if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); + findtrytb += trytb.CsName; + + var trycol = bindColumns.Any() ? bindColumns[a] : null; + if (trycol == null && + tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}", out trycol) == false //下划线命名 + ) + { + if (refprop != null && + tbref.ColumnsByCs.TryGetValue($"{refprop.Name}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{refprop.Name}_{findtrytbPkCsName}", out trycol) == false) //下划线命名 + { + + } + } + if (trycol != null && trycol.CsType.NullableTypeOrThis() != trytb.Primarys[a].CsType.NullableTypeOrThis()) + { + nvref.Exception = new Exception(CoreStrings.OneToMany_ParsingError_InconsistentType(trytbTypeName, pnv.Name, trytb.CsName, trytb.Primarys[a].CsName, tbref.CsName, trycol.CsName)); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + break; + } + if (trycol == null) + { + nvref.Exception = new Exception(CoreStrings.OneToMany_NotFound_CorrespondingField(trytbTypeName, pnv.Name, tbref.CsName, findtrytb, findtrytbPkCsName) + + (refprop == null ? "" : CoreStrings.OneToMany_UseNavigate(refprop.Name, findtrytbPkCsName))); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + break; + } - nvref.Columns.Add(trytb.Primarys[a]); - nvref.RefColumns.Add(trycol); + nvref.Columns.Add(trytb.Primarys[a]); + nvref.RefColumns.Add(trycol); - if (isLazy && nvref.Exception == null) - { - if (a > 0) lmbdWhere.Append(" && "); - lmbdWhere.Append("a.").Append(trycol.CsName).Append(" == this.").Append(trytb.Primarys[a].CsName); + if (isLazy && nvref.Exception == null) + { + if (a > 0) lmbdWhere.Append(" && "); + lmbdWhere.Append("a.").Append(trycol.CsName).Append(" == this.").Append(trytb.Primarys[a].CsName); - if (refprop == null) - { //加载成功后,把列表对应的导航属性值设置为 this,比如 Select().ToOne().Topics 下的 TopicType 属性值全部为 this - var findtrytbName = trycol.CsName; - if (findtrytbName.EndsWith(trytb.Primarys.First().CsName)) - { - findtrytbName = findtrytbName.Remove(findtrytbName.Length - trytb.Primarys.First().CsName.Length).TrimEnd('_'); - if (tbref.Properties.TryGetValue(findtrytbName, out refprop) && refprop.PropertyType != trytb.Type) - refprop = null; + if (refprop == null) + { //加载成功后,把列表对应的导航属性值设置为 this,比如 Select().ToOne().Topics 下的 TopicType 属性值全部为 this + var findtrytbName = trycol.CsName; + if (findtrytbName.EndsWith(trytb.Primarys.First().CsName)) + { + findtrytbName = findtrytbName.Remove(findtrytbName.Length - trytb.Primarys.First().CsName.Length).TrimEnd('_'); + if (tbref.Properties.TryGetValue(findtrytbName, out refprop) && refprop.PropertyType != trytb.Type) + refprop = null; + } } } } - } - if (nvref.Columns.Count > 0 && nvref.RefColumns.Count > 0) - { - nvref.RefEntityType = tbref.Type; - nvref.RefType = TableRefType.OneToMany; - trytb.AddOrUpdateTableRef(pnv.Name, nvref); - } + if (nvref.Columns.Count > 0 && nvref.RefColumns.Count > 0) + { + nvref.RefEntityType = tbref.Type; + nvref.RefType = TableRefType.OneToMany; + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + } + } if (isLazy) { cscode.Append(" private bool __lazy__").Append(pnv.Name).AppendLine(" = false;") @@ -1133,7 +1237,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo } } else - { //一对一、多对一 + { + //一对一、多对一 var tbref = pnv.PropertyType == trytb.Type ? trytb : GetTableByEntity(pnv.PropertyType, common); //可能是父子关系 if (tbref == null) return; if (tbref.Primarys.Any() == false) From 710b88e914933b3126bbe2122ce1d9a7d97bbf8e Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 4 Jun 2022 12:26:00 +0800 Subject: [PATCH 02/20] add ArrayToMany IncludeMany --- .../SelectProvider/Select1Provider.cs | 123 +++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 36cde5e9d..04e2b7119 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -670,10 +670,11 @@ public ISelect IncludeMany(Expression); var listValueExp = Expression.Parameter(typeof(List), "listValue"); var setListValue = membersExpNotNull == null ? Expression.Lambda>>( - collMem.Type == typeof(ObservableCollection) ? + isObservableCollection ? (Expression)Expression.IfThen( Expression.NotEqual(listValueExp, Expression.Constant(null, typeof(List))), Expression.Assign(Expression.MakeMemberAccess(membersExp, collMem.Member), Expression.New(typeof(ObservableCollection).GetConstructor(new[] { typeof(List) }), listValueExp)) @@ -681,7 +682,7 @@ public ISelect IncludeMany(Expression>>(Expression.IfThen(membersExpNotNull, - collMem.Type == typeof(ObservableCollection) ? + isObservableCollection ? (Expression)Expression.IfThen( Expression.NotEqual(listValueExp, Expression.Constant(null, typeof(List))), Expression.Assign(Expression.MakeMemberAccess(membersExp, collMem.Member), Expression.New(typeof(ObservableCollection).GetConstructor(new[] { typeof(List) }), listValueExp)) @@ -1175,6 +1176,124 @@ public ISelect IncludeMany(Expression(); + var tbref2 = _commonUtils.GetTableByEntity(tbref.RefEntityType); + if (tbref.RefColumns[0] == tbref2.Primarys[0]) + { + var listKeys = list.Select(a => + { + var arrVal = getListValue(a, tbref.Columns[0].CsName, 0) as Array; + var arrObjVal = new object[arrVal.Length]; + arrVal.CopyTo(arrObjVal, 0); + return arrObjVal; + }).Where(a => a != null).ToArray(); + var arrExp = Expression.NewArrayInit(tbref.RefColumns[0].CsType, listKeys.SelectMany(a => a).Distinct() + .Select(a => Expression.Constant(Utils.GetDataReaderValue(tbref.RefColumns[0].CsType, a), tbref.RefColumns[0].CsType)).ToArray()); + var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); + var containsMethod = _dicTypeMethod.GetOrAdd(tbref.RefColumns[0].CsType, et => new ConcurrentDictionary()).GetOrAdd("Contains", mn => + typeof(Enumerable).GetMethods().Where(a => a.Name == mn).First()).MakeGenericMethod(tbref.RefColumns[0].CsType); + var refCol = Expression.MakeMemberAccess(otmExpParm1, tbref2.Properties[tbref.RefColumns[0].CsName]); + subSelect.Where(Expression.Lambda>( + Expression.Call(null, containsMethod, arrExp, refCol), otmExpParm1)); + + if (isAsync) + { +#if net40 +#else + if (selectExp == null) subList = await subSelect.ToListAsync(true, cancellationToken); + else subList = await subSelect.ToListAsync(selectExp, cancellationToken); +#endif + } + else + { + if (selectExp == null) subList = subSelect.ToList(true); + else subList = subSelect.ToList(selectExp); + } + + if (subList.Any() == false) + { + foreach (var item in list) + setListValue(item, new List()); + return; + } + var dicSubList = subList.ToDictionary(a => EntityUtilExtensions.GetEntityValueWithPropertyName(_orm, tbref.RefEntityType, a, tbref.RefColumns[0].CsName)?.ToString(), a => a); + + var parentNavs = new List(); + foreach (var navProp in tbref2.Properties) + { + if (tbref2.ColumnsByCs.ContainsKey(navProp.Key)) continue; + if (tbref2.ColumnsByCsIgnore.ContainsKey(navProp.Key)) continue; + var tr2ref = tbref2.GetTableRef(navProp.Key, false); + if (tr2ref == null) continue; + if (tr2ref.RefType != TableRefType.ManyToOne) continue; + if (tr2ref.RefEntityType != tb.Type) continue; + if (string.Join(",", tr2ref.Columns.Select(a => a.CsName).OrderBy(a => a)) != string.Join(",", tbref.RefColumns.Select(a => a.CsName).OrderBy(a => a))) continue; //- 修复 IncludeMany 只填充子属性中双向关系的 ManyToOne 对象值;防止把 ManyToOne 多个相同类型的导航属性值都填充了 + parentNavs.Add(navProp.Key); + } + for (var y = 0; y < list.Count; y++) + { + var item = list[y]; + var dicListKeys = listKeys[y]; + var navs = new List(); + foreach (var dlk in dicListKeys) + { + if (dlk == null) + { + navs.Add(null); + continue; + } + var dicListKey = dlk.ToString(); + dicSubList.TryGetValue(dicListKey, out var nav); + navs.Add(nav); + } + setListValue(item, navs); + } + dicSubList.Clear(); + subList.Clear(); + } + else if (tbref.Columns[0] == tb.Primarys[0]) + { + var listKeys = list.Select(a => getListValue(a, tbref.Columns[0].CsName, 0)).Distinct() + .Select(a => Utils.GetDataReaderValue(tbref.RefColumns[0].CsType.GetElementType(), a)).ToArray(); + var listKeysSql = _commonUtils.GetNoneParamaterSqlValue(subSelect._params, "arrtm", tbref.RefColumns[0], tbref.RefColumns[0].CsType, listKeys); + subSelect.Where($"{subSelectT1Alias}.{_commonUtils.QuoteSqlName(tbref.RefColumns[0].Attribute.Name)} && {listKeysSql}"); + + if (isAsync) + { +#if net40 +#else + if (selectExp == null) subList = await subSelect.ToListAsync(true, cancellationToken); + else subList = await subSelect.ToListAsync(selectExp, cancellationToken); +#endif + } + else + { + if (selectExp == null) subList = subSelect.ToList(true); + else subList = subSelect.ToList(selectExp); + } + + if (subList.Any() == false) + { + foreach (var item in list) + setListValue(item, new List()); + return; + } + var subListDic = subList.Select(a => { + var arrVal = EntityUtilExtensions.GetEntityValueWithPropertyName(_orm, tbref2.Type, a, tbref.RefColumns[0].CsName) as Array; + var arrObjVal = new object[arrVal.Length]; + arrVal.CopyTo(arrObjVal, 0); + return arrObjVal.Select(b => NativeTuple.Create(a, b?.ToString())); + }).SelectMany(a => a).GroupBy(a => a.Item2).ToDictionary(a => a.Key, a => a.Select(b => b.Item1).ToList()); + foreach (var item in list) + { + var itemKey = getListValue(item, tbref.Columns[0].CsName, 0)?.ToString(); + subListDic.TryGetValue(itemKey, out var navs); + setListValue(item, navs); + } + } + } break; } }; From 53bea5beb1f522546c086fddc2da93c96188428f Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 4 Jun 2022 15:23:24 +0800 Subject: [PATCH 03/20] rename ArrayToMany to PgArrayToMany --- FreeSql.DbContext/DbSet/DbSetAsync.cs | 4 ++-- FreeSql.DbContext/DbSet/DbSetSync.cs | 4 ++-- FreeSql/Internal/CommonExpression.cs | 14 +++++++++--- .../SelectProvider/Select1Provider.cs | 7 +++--- FreeSql/Internal/Model/TableInfo.cs | 2 +- FreeSql/Internal/UtilsExpressionTree.cs | 22 ++++++++++++++----- 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/FreeSql.DbContext/DbSet/DbSetAsync.cs b/FreeSql.DbContext/DbSet/DbSetAsync.cs index 5bdccd0cf..ea5d35d18 100644 --- a/FreeSql.DbContext/DbSet/DbSetAsync.cs +++ b/FreeSql.DbContext/DbSet/DbSetAsync.cs @@ -157,7 +157,7 @@ async public Task SaveManyAsync(TEntity item, string propertyName, CancellationT { case TableRefType.OneToOne: case TableRefType.ManyToOne: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: throw new ArgumentException(DbContextStrings.PropertyOfType_IsNot_OneToManyOrManyToMany(_table.Type.FullName, propertyName)); } @@ -227,7 +227,7 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam else await refSet.AddOrUpdateAsync(propValItem); return; case TableRefType.ManyToOne: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: return; } diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index fc9bb1e10..949753555 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -168,7 +168,7 @@ public void SaveMany(TEntity item, string propertyName) { case TableRefType.OneToOne: case TableRefType.ManyToOne: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: throw new ArgumentException(DbContextStrings.PropertyOfType_IsNot_OneToManyOrManyToMany(_table.Type.FullName, propertyName)); } @@ -238,7 +238,7 @@ void AddOrUpdateNavigate(TEntity item, bool isAdd, string propertyName) else refSet.AddOrUpdate(propValItem); return; case TableRefType.ManyToOne: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: return; } diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index eefbd8530..428efb503 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -154,7 +154,7 @@ public bool ReadAnonymousField(List _tables, StringBuilder fiel { case TableRefType.ManyToMany: case TableRefType.OneToMany: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: continue; } if (_tables.Any(a => a.Alias == $"{map.First().Table.Alias}__{memProp.Name}") == false) continue; @@ -2195,7 +2195,13 @@ void LocalSetSelectProviderAlias(string alias) for (var tidx = 0; tidx < memberTbref.Columns.Count; tidx++) select.Where($"{select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[tidx].Attribute.Name)} = {omtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[tidx].Attribute.Name)}"); break; - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: + var amtReftbname = e.FreeParse(Expression.MakeMemberAccess(memberExp.Expression, exp3Tb.Properties[exp3Tb.ColumnsByPosition[0].CsName])); + amtReftbname = amtReftbname.Substring(0, amtReftbname.Length - commonExp._common.QuoteSqlName(exp3Tb.ColumnsByPosition[0].Attribute.Name).Length - 1); + if (memberTbref.RefColumns[0] == select._tables[0].Table.Primarys[0]) + select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)} @> {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)}"); + else if (memberTbref.Columns[0] == select._tables[0].Table.Primarys[0]) + select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)} @> {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)}"); break; } @@ -2244,7 +2250,9 @@ void LocalInitSelectProvider() if (select != null) return; LocalInitSelectProvider(); continue; - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: + if (select != null) return; + LocalInitSelectProvider(); continue; } } diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 04e2b7119..838e01ebf 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -418,6 +418,7 @@ public ISelect IncludeByPropertyName(string property) { case TableRefType.ManyToMany: case TableRefType.OneToMany: + case TableRefType.PgArrayToMany: var funcType = typeof(Func<,>).MakeGenericType(_tables[0].Table.Type, typeof(IEnumerable<>).MakeGenericType(parTbref.RefEntityType)); var navigateSelector = Expression.Lambda(funcType, exp, _tables[0].Parameter); var incMethod = this.GetType().GetMethod("IncludeMany"); @@ -430,8 +431,6 @@ public ISelect IncludeByPropertyName(string property) var curTb = _commonUtils.GetTableByEntity(exp.Type); _commonExpression.ExpressionWhereLambda(_tables, Expression.MakeMemberAccess(exp, curTb.Properties[curTb.ColumnsByCs.First().Value.CsName]), null, null, null); break; - case TableRefType.ArrayToMany: - break; } return this; } @@ -1175,7 +1174,7 @@ public ISelect IncludeMany(Expression(); @@ -1256,7 +1255,7 @@ public ISelect IncludeMany(Expression getListValue(a, tbref.Columns[0].CsName, 0)).Distinct() - .Select(a => Utils.GetDataReaderValue(tbref.RefColumns[0].CsType.GetElementType(), a)).ToArray(); + .Select(a => Utils.GetDataReaderValue(tbref.RefColumns[0].CsType.GetElementType(), a)).ToArray(); var listKeysSql = _commonUtils.GetNoneParamaterSqlValue(subSelect._params, "arrtm", tbref.RefColumns[0], tbref.RefColumns[0].CsType, listKeys); subSelect.Where($"{subSelectT1Alias}.{_commonUtils.QuoteSqlName(tbref.RefColumns[0].Attribute.Name)} && {listKeysSql}"); diff --git a/FreeSql/Internal/Model/TableInfo.cs b/FreeSql/Internal/Model/TableInfo.cs index 2496d63c2..1349e952c 100644 --- a/FreeSql/Internal/Model/TableInfo.cs +++ b/FreeSql/Internal/Model/TableInfo.cs @@ -150,6 +150,6 @@ public class TableRef } public enum TableRefType { - OneToOne, ManyToOne, OneToMany, ManyToMany, ArrayToMany + OneToOne, ManyToOne, OneToMany, ManyToMany, PgArrayToMany } } \ No newline at end of file diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 271edc49c..73163bcc3 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Data; using System.Data.Common; using System.Linq; @@ -623,6 +624,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo //List 或 ICollection,一对多、多对多 var propElementType = pnv.PropertyType.GetGenericArguments().FirstOrDefault() ?? pnv.PropertyType.GetElementType(); + var propTypeIsObservableCollection = propElementType != null && pnv.PropertyType == typeof(ObservableCollection<>).MakeGenericType(propElementType); if (propElementType != null) { if (typeof(IEnumerable).IsAssignableFrom(pnv.PropertyType) == false) return; @@ -992,17 +994,18 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo { var isArrayToMany = false; var lmbdWhere = isLazy ? new StringBuilder() : null; + var cscodeExtLogic = ""; //Pgsql Array[] To Many if (common._orm.Ado.DataType == DataType.PostgreSQL) { //class User { // public int[] RoleIds { get; set; } // [Navigate(nameof(RoleIds))] - // public Role[] Roles { get; set; } + // public List Roles { get; set; } //} //class Role { // [Navigate(nameof(User.RoleIds))] - // public User[] Users { get; set; } + // public List Users { get; set; } //} ColumnInfo trycol = null; if (tbref.Primarys.Length == 1) @@ -1038,11 +1041,14 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName).Append(")"); + cscodeExtLogic = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; + lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName); + if (trycol.CsType.GetElementType().IsNullableType() == false && tbref.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); + lmbdWhere.Append(")"); nvref.Columns.Add(trycol); nvref.RefColumns.Add(tbref.Primarys[0]); nvref.RefEntityType = tbref.Type; - nvref.RefType = TableRefType.ArrayToMany; + nvref.RefType = TableRefType.PgArrayToMany; trytb.AddOrUpdateTableRef(pnv.Name, nvref); } } @@ -1080,11 +1086,14 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName).Append(")"); + cscodeExtLogic = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName); + if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); + lmbdWhere.Append(")"); nvref.Columns.Add(tbref.Primarys[0]); nvref.RefColumns.Add(trycol); nvref.RefEntityType = tbref.Type; - nvref.RefType = TableRefType.ArrayToMany; + nvref.RefType = TableRefType.PgArrayToMany; trytb.AddOrUpdateTableRef(pnv.Name, nvref); } } @@ -1205,6 +1214,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo if (vp?.Item2 == true) { //get 重写 cscode.Append(" ").Append(propGetModification).Append(" get {\r\n") + .Append(cscodeExtLogic) .Append(" if (base.").Append(pnv.Name).Append(" == null && __lazy__").Append(pnv.Name).AppendLine(" == false) {"); if (nvref.Exception == null) From 7839b42ac2c45b9cf3efa937c21e282ac4784053 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 4 Jun 2022 16:10:20 +0800 Subject: [PATCH 04/20] fix PgArrayToMany --- FreeSql.Tests/FreeSql.Tests/Issues/623.cs | 2 +- FreeSql/Extensions/FreeSqlGlobalExtensions.cs | 2 +- FreeSql/FreeSql.xml | 17 +++++++++++++++++ FreeSql/Internal/Model/TableInfo.cs | 18 +++++++++++++++++- FreeSql/Internal/UtilsExpressionTree.cs | 17 +++++++++++------ 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/Issues/623.cs b/FreeSql.Tests/FreeSql.Tests/Issues/623.cs index db0da73ba..5898fc433 100644 --- a/FreeSql.Tests/FreeSql.Tests/Issues/623.cs +++ b/FreeSql.Tests/FreeSql.Tests/Issues/623.cs @@ -44,7 +44,7 @@ public void MySqlGroupBy() FacilityCount = a.Sum(a.Value.FacilityCount), FacilityOpenCount = a.Sum(a.Value.FacilityOpenCount) }); - Assert.Equal(@"SELECT a.`Dot`, sum(a.`FacilityCount`) as1, sum(a.`FacilityOpenCount`) as2 + Assert.Equal(@"SELECT a.`Dot` as1, sum(a.`FacilityCount`) as2, sum(a.`FacilityOpenCount`) as3 FROM `ts_facility` a WHERE (a.`Date` = cast(date_format('2020-12-30 00:00:00.000','%Y-%m-%d') as datetime)) AND (((a.`EnterpriseId`) in (5))) AND (a.`FacilityType` = 1) GROUP BY a.`Dot`", sql); diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs index e0ffb9c1a..9124dd68f 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs @@ -48,7 +48,7 @@ public static partial class FreeSqlGlobalExtensions }); public static bool IsIntegerType(this Type that) => that == null ? false : (_dicIsNumberType.Value.TryGetValue(that, out var tryval) ? tryval : false); public static bool IsNumberType(this Type that) => that == null ? false : _dicIsNumberType.Value.ContainsKey(that); - public static bool IsNullableType(this Type that) => that.IsArray == false && that?.FullName.StartsWith("System.Nullable`1[") == true; + public static bool IsNullableType(this Type that) => that == null ? false : (that.IsArray == false && that.FullName.StartsWith("System.Nullable`1[") == true); public static bool IsAnonymousType(this Type that) => that == null ? false : (that.FullName.StartsWith("<>f__AnonymousType") || that.FullName.StartsWith("VB$AnonymousType")); public static bool IsArrayOrList(this Type that) => that == null ? false : (that.IsArray || typeof(IList).IsAssignableFrom(that)); public static Type NullableTypeOrThis(this Type that) => that?.IsNullableType() == true ? that.GetGenericArguments().First() : that; diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 2db205b70..434ec7a25 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -4440,6 +4440,23 @@ 中间表,多对多 + + + PostgreSQL 数组类型专属功能 + 方式一:select * from Role where Id in (RoleIds) + class User { + ____public int[] RoleIds { get; set; } + ____[Navigate(nameof(RoleIds))] + ____public List<Role> Roles { get; set; } + } + 方式二:select * from User where RoleIds @> Id + class Role { + ____public int Id { get; set; } + ____[Navigate(nameof(User.RoleIds))] + ____public List<User> Users { get; set; } + } + + 是否可用 diff --git a/FreeSql/Internal/Model/TableInfo.cs b/FreeSql/Internal/Model/TableInfo.cs index 1349e952c..6c8583149 100644 --- a/FreeSql/Internal/Model/TableInfo.cs +++ b/FreeSql/Internal/Model/TableInfo.cs @@ -150,6 +150,22 @@ public class TableRef } public enum TableRefType { - OneToOne, ManyToOne, OneToMany, ManyToMany, PgArrayToMany + OneToOne, ManyToOne, OneToMany, ManyToMany, + /// + /// PostgreSQL 数组类型专属功能 + /// 方式一:select * from Role where Id in (RoleIds) + /// class User { + /// ____public int[] RoleIds { get; set; } + /// ____[Navigate(nameof(RoleIds))] + /// ____public List<Role> Roles { get; set; } + /// } + /// 方式二:select * from User where RoleIds @> Id + /// class Role { + /// ____public int Id { get; set; } + /// ____[Navigate(nameof(User.RoleIds))] + /// ____public List<User> Users { get; set; } + /// } + /// + PgArrayToMany } } \ No newline at end of file diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 73163bcc3..7e852d3be 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -1014,7 +1014,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo { if (trytb.ColumnsByCs.TryGetValue(pnvBind[0], out trycol)) { - if (trycol.CsType.IsArray == true && tbref.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + if (trycol.CsType.IsArray == false) trycol = null; + else if (trycol != null && tbref.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) { nvref.Exception = new Exception($"导航属性 {trytbTypeName}.{pnv.Name} 特性 [Navigate] 解析错误,{trytbTypeName}.{trycol.CsName} 数组元素 与 {tbrefTypeName}.{tbref.Primarys[0].CsName} 类型不符"); trytb.AddOrUpdateTableRef(pnv.Name, nvref); @@ -1022,7 +1023,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo } } } - if (nvref.Exception == null && trycol == null) + if (pnvBind == null && trycol == null) { var findtbrefPkCsName = tbref.Primarys[0].CsName.TrimStart('_'); if (findtbrefPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtbrefPkCsName = findtbrefPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); @@ -1059,7 +1060,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo { if (tbref.ColumnsByCs.TryGetValue(pnvBind[0], out trycol)) { - if (trycol.CsType.IsArray == true && trytb.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + if (trycol.CsType.IsArray == false) trycol = null; + else if (trytb.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) { nvref.Exception = new Exception($"导航属性 {trytbTypeName}.{pnv.Name} 特性 [Navigate] 解析错误,{trytbTypeName}.{trytb.Primarys[0].CsName} 与 {tbrefTypeName}.{trycol.CsName} 数组元素类型不符"); trytb.AddOrUpdateTableRef(pnv.Name, nvref); @@ -1067,7 +1069,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo } } } - if (nvref.Exception == null && trycol == null) + if (pnvBind != null && trycol == null) { var findtrytbPkCsName = trytb.Primarys[0].CsName.TrimStart('_'); if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); @@ -1086,9 +1088,12 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - cscodeExtLogic = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName); - if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); + if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) + { + lmbdWhere.Append(".Value"); + cscodeExtLogic = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + } lmbdWhere.Append(")"); nvref.Columns.Add(tbref.Primarys[0]); nvref.RefColumns.Add(trycol); From c205addd81a16028354d28f4b5dc56da23eb4612 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 13:36:18 +0800 Subject: [PATCH 05/20] =?UTF-8?q?-=20=E5=A4=84=E7=90=86=20PgArrayToMany=20?= =?UTF-8?q?AsSelect=20=E5=AD=90=E6=9F=A5=E8=AF=A2=20#1145?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.DbContext/DbSet/DbSetSync.cs | 6 + FreeSql/Internal/CommonExpression.cs | 276 ++++++++++++++------------- 2 files changed, 151 insertions(+), 131 deletions(-) diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index 949753555..cc6f8a3e4 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -862,6 +862,12 @@ void LocalEach(DbSet dbset, IEnumerable items, bool isOneToOne) } } + var atms = navs.Where(a => a.Item1.RefType == TableRefType.PgArrayToMany).ToList(); + if (atms.Any()) + { + + } + if (dbset == rootDbSet) { if (CanRemove(data, true) == false) return; diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 428efb503..b7918b54e 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -1195,162 +1195,176 @@ public string ExpressionLambdaToSql(Expression exp, ExpTSC tsc) if (fsql != null) { if (asSelectParentExp != null) - { //执行 AsSelect() 的关联,OneToMany,ManyToMany + { + //执行 AsSelect() 的关联,OneToMany,ManyToMany,PgArrayToMany if (fsqltables[0].Parameter == null) { fsqltables[0].Alias = $"tb_{fsqltables.Count}"; fsqltables[0].Parameter = Expression.Parameter(asSelectEntityType, fsqltables[0].Alias); } - var fsqlWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(asSelectEntityType, asSelectEntityType3 => - typeof(ISelect<>).MakeGenericType(asSelectEntityType3).GetMethod("Where", new[] { - typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(asSelectEntityType3, typeof(bool))) - })); + var parm123Tb = _common.GetTableByEntity(asSelectParentExp.Type); var parm123Ref = parm123Tb.GetTableRef(asSelectParentExp1.Member.Name, true); if (parm123Ref != null) { - var fsqlWhereParam = fsqltables.First().Parameter; //Expression.Parameter(asSelectEntityType); - Expression fsqlWhereExp = null; - if (parm123Ref.RefType == TableRefType.ManyToMany) + if (parm123Ref.RefType == TableRefType.PgArrayToMany) { - //g.mysql.Select().Where(a => g.mysql.Select().Where(b => b.Tag_id == a.Id && b.Song_id == 1).Any()); - var manyTb = _common.GetTableByEntity(parm123Ref.RefMiddleEntityType); - var manySubSelectWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => - typeof(ISelect<>).MakeGenericType(refMiddleEntityType3).GetMethod("Where", new[] { - typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(refMiddleEntityType3, typeof(bool))) - })); - var manySubSelectWhereSql = _dicExpressionLambdaToSqlAsSelectWhereSqlMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => - typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(refMiddleEntityType3), refMiddleEntityType3).GetMethod("Where", new[] { typeof(string), typeof(object) })); - var manySubSelectAsSelectExp = _dicFreeSqlGlobalExtensionsAsSelectExpression.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => - Expression.Call( - typeof(FreeSqlGlobalExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(mfil => mfil.Name == "AsSelect" && mfil.GetParameters().Length == 1).FirstOrDefault()?.MakeGenericMethod(refMiddleEntityType3), - Expression.Constant(Activator.CreateInstance(typeof(List<>).MakeGenericType(refMiddleEntityType3))) - )); - var manyMainParam = tsc._tables[0].Parameter; - var manySubSelectWhereParam = Expression.Parameter(parm123Ref.RefMiddleEntityType, $"M{fsqlWhereParam.Name}_M{asSelectParentExp.ToString().Replace(".", "__")}");//, $"{fsqlWhereParam.Name}__"); - Expression manySubSelectWhereExp = null; - for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) + var amtReftbname = ExpressionLambdaToSql(Expression.MakeMemberAccess(asSelectParentExp, parm123Tb.Properties[parm123Tb.ColumnsByPosition[0].CsName]), tsc); + amtReftbname = amtReftbname.Substring(0, amtReftbname.Length - _common.QuoteSqlName(parm123Tb.ColumnsByPosition[0].Attribute.Name).Length - 1); + if (parm123Ref.RefColumns[0] == fsqltables[0].Table.Primarys[0]) + (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)} @> {fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)}").Append(")"); + else if (parm123Ref.Columns[0] == fsqltables[0].Table.Primarys[0]) + (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)} @> {fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)}").Append(")"); + } + else + { + var fsqlWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(asSelectEntityType, asSelectEntityType3 => + typeof(ISelect<>).MakeGenericType(asSelectEntityType3).GetMethod("Where", new[] { + typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(asSelectEntityType3, typeof(bool))) + })); + var fsqlWhereParam = fsqltables.First().Parameter; //Expression.Parameter(asSelectEntityType); + Expression fsqlWhereExp = null; + if (parm123Ref.RefType == TableRefType.ManyToMany) { - var col1 = parm123Ref.MiddleColumns[mn]; - var col2 = parm123Ref.Columns[mn]; - var pexp1 = Expression.Property(manySubSelectWhereParam, col1.CsName); - var pexp2 = Expression.Property(asSelectParentExp, col2.CsName); - if (col1.CsType != col2.CsType) + //g.mysql.Select().Where(a => g.mysql.Select().Where(b => b.Tag_id == a.Id && b.Song_id == 1).Any()); + var manyTb = _common.GetTableByEntity(parm123Ref.RefMiddleEntityType); + var manySubSelectWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => + typeof(ISelect<>).MakeGenericType(refMiddleEntityType3).GetMethod("Where", new[] { + typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(refMiddleEntityType3, typeof(bool))) + })); + var manySubSelectWhereSql = _dicExpressionLambdaToSqlAsSelectWhereSqlMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => + typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(refMiddleEntityType3), refMiddleEntityType3).GetMethod("Where", new[] { typeof(string), typeof(object) })); + var manySubSelectAsSelectExp = _dicFreeSqlGlobalExtensionsAsSelectExpression.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => + Expression.Call( + typeof(FreeSqlGlobalExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(mfil => mfil.Name == "AsSelect" && mfil.GetParameters().Length == 1).FirstOrDefault()?.MakeGenericMethod(refMiddleEntityType3), + Expression.Constant(Activator.CreateInstance(typeof(List<>).MakeGenericType(refMiddleEntityType3))) + )); + var manyMainParam = tsc._tables[0].Parameter; + var manySubSelectWhereParam = Expression.Parameter(parm123Ref.RefMiddleEntityType, $"M{fsqlWhereParam.Name}_M{asSelectParentExp.ToString().Replace(".", "__")}");//, $"{fsqlWhereParam.Name}__"); + Expression manySubSelectWhereExp = null; + for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) { - if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); - if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); + var col1 = parm123Ref.MiddleColumns[mn]; + var col2 = parm123Ref.Columns[mn]; + var pexp1 = Expression.Property(manySubSelectWhereParam, col1.CsName); + var pexp2 = Expression.Property(asSelectParentExp, col2.CsName); + if (col1.CsType != col2.CsType) + { + if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); + if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); + } + var tmpExp = Expression.Equal(pexp1, pexp2); + if (mn == 0) manySubSelectWhereExp = tmpExp; + else manySubSelectWhereExp = Expression.AndAlso(manySubSelectWhereExp, tmpExp); } - var tmpExp = Expression.Equal(pexp1, pexp2); - if (mn == 0) manySubSelectWhereExp = tmpExp; - else manySubSelectWhereExp = Expression.AndAlso(manySubSelectWhereExp, tmpExp); + var manySubSelectExpBoy = Expression.Call( + manySubSelectAsSelectExp, + manySubSelectWhere, + Expression.Lambda( + manySubSelectWhereExp, + manySubSelectWhereParam + ) + ); + Expression fsqlManyWhereExp = null; + for (var mn = 0; mn < parm123Ref.RefColumns.Count; mn++) + { + var col1 = parm123Ref.RefColumns[mn]; + var col2 = parm123Ref.MiddleColumns[mn + parm123Ref.Columns.Count + mn]; + var pexp1 = Expression.Property(fsqlWhereParam, col1.CsName); + var pexp2 = Expression.Property(manySubSelectWhereParam, col2.CsName); + if (col1.CsType != col2.CsType) + { + if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); + if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); + } + var tmpExp = Expression.Equal(pexp1, pexp2); + if (mn == 0) fsqlManyWhereExp = tmpExp; + else fsqlManyWhereExp = Expression.AndAlso(fsqlManyWhereExp, tmpExp); + } + MethodInfo manySubSelectAggMethod = null; + switch (exp3.Method.Name) //https://github.com/dotnetcore/FreeSql/issues/362 + { + case "Any": + case "Count": + fsqltables.Add(new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.Parent }); + fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); + var sql2 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { "1" })?.ToString(); + if (string.IsNullOrEmpty(sql2) == false) + manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectWhereSql, Expression.Constant($"exists({sql2.Replace(" \r\n", " \r\n ")})"), Expression.Constant(null)); + manySubSelectAggMethod = _dicExpressionLambdaToSqlAsSelectAggMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, _ => new ConcurrentDictionary()).GetOrAdd(exp3.Method.Name, exp3MethodName => + typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(parm123Ref.RefMiddleEntityType), parm123Ref.RefMiddleEntityType).GetMethod(exp3MethodName, new Type[0])); + manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectAggMethod); + break; + case "Sum": + case "Min": + case "Max": + case "Avg": + case "ToList": + case "ToOne": + case "First": + //解析:string.Join(",", w.Roles.AsSelect().ToList(b => b.RoleName) + var exp3Args0 = (exp3.Arguments[0] as UnaryExpression)?.Operand as LambdaExpression; + manySubSelectAggMethod = _dicSelectMethodToSql.GetOrAdd(fsqlType, fsqlType2 => + fsqlType2.GetMethods().Where(a => a.Name == "ToSql" && a.GetParameters().Length == 2 && a.GetParameters()[1].ParameterType == typeof(FieldAliasOptions) && a.GetGenericArguments().Length == 1).FirstOrDefault()); + if (manySubSelectAggMethod == null || exp3Args0 == null) throw new ArgumentException(CoreStrings.ManyToMany_AsSelect_NotSupport_Sum_Avg_etc); + manySubSelectAggMethod = manySubSelectAggMethod.MakeGenericMethod(exp3Args0.ReturnType); + var fsqls0p = fsql as Select0Provider; + var fsqls0pWhere = fsqls0p._where.ToString(); + fsqls0p._where.Clear(); + var fsqltablesLast = new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.InnerJoin }; + fsqltables.Add(fsqltablesLast); + fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); + fsqltablesLast.NavigateCondition = fsqls0p._where.ToString(); + if (fsqltablesLast.NavigateCondition.StartsWith(" AND (")) fsqltablesLast.NavigateCondition = fsqltablesLast.NavigateCondition.Substring(6, fsqltablesLast.NavigateCondition.Length - 7); + fsqls0p._where.Clear().Append(fsqls0pWhere); + var tsc3 = tsc.CloneDisableDiyParse(); + tsc3._tables = tsc._tables.ToList(); + var where2 = ExpressionLambdaToSql(Expression.Lambda(manySubSelectWhereExp, manySubSelectWhereParam), tsc3); + if (string.IsNullOrEmpty(where2) == false) fsqls0p._where.Append(" AND (").Append(where2).Append(")"); + + switch (exp3.Method.Name) + { + case "Sum": + case "Min": + case "Max": + case "Avg": + var map = new ReadAnonymousTypeInfo(); + var field = new StringBuilder(); + var index = -1; + + for (var a = 0; a < exp3Args0.Parameters.Count; a++) fsqls0p._tables[a].Parameter = exp3Args0.Parameters[a]; + ReadAnonymousField(fsqls0p._tables, field, map, ref index, exp3Args0, null, null, null, null, null, false); + var fieldSql = field.Length > 0 ? field.Remove(0, 2).ToString() : null; + + var sql4 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { $"{exp3.Method.Name.ToLower()}({fieldSql})" })?.ToString(); + asSelectBefores.Clear(); + return _common.IsNull($"({sql4.Replace(" \r\n", " \r\n ")})", formatSql(exp3.Method.ReturnType.CreateInstanceGetDefaultValue(), exp3.Method.ReturnType, null, null)); + } + + var sql3 = manySubSelectAggMethod.Invoke(fsql, new object[] { exp3Args0, FieldAliasOptions.AsProperty }) as string; + asSelectBefores.Clear(); + return $"({sql3.Replace(" \r\n", " \r\n ")})"; + } + asSelectBefores.Clear(); + return ExpressionLambdaToSql(manySubSelectExpBoy, tsc); } - var manySubSelectExpBoy = Expression.Call( - manySubSelectAsSelectExp, - manySubSelectWhere, - Expression.Lambda( - manySubSelectWhereExp, - manySubSelectWhereParam - ) - ); - Expression fsqlManyWhereExp = null; - for (var mn = 0; mn < parm123Ref.RefColumns.Count; mn++) + for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) { var col1 = parm123Ref.RefColumns[mn]; - var col2 = parm123Ref.MiddleColumns[mn + parm123Ref.Columns.Count + mn]; + var col2 = parm123Ref.Columns[mn]; var pexp1 = Expression.Property(fsqlWhereParam, col1.CsName); - var pexp2 = Expression.Property(manySubSelectWhereParam, col2.CsName); + var pexp2 = Expression.Property(asSelectParentExp, col2.CsName); if (col1.CsType != col2.CsType) { if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); } var tmpExp = Expression.Equal(pexp1, pexp2); - if (mn == 0) fsqlManyWhereExp = tmpExp; - else fsqlManyWhereExp = Expression.AndAlso(fsqlManyWhereExp, tmpExp); - } - MethodInfo manySubSelectAggMethod = null; - switch (exp3.Method.Name) //https://github.com/dotnetcore/FreeSql/issues/362 - { - case "Any": - case "Count": - fsqltables.Add(new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.Parent }); - fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); - var sql2 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { "1" })?.ToString(); - if (string.IsNullOrEmpty(sql2) == false) - manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectWhereSql, Expression.Constant($"exists({sql2.Replace(" \r\n", " \r\n ")})"), Expression.Constant(null)); - manySubSelectAggMethod = _dicExpressionLambdaToSqlAsSelectAggMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, _ => new ConcurrentDictionary()).GetOrAdd(exp3.Method.Name, exp3MethodName => - typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(parm123Ref.RefMiddleEntityType), parm123Ref.RefMiddleEntityType).GetMethod(exp3MethodName, new Type[0])); - manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectAggMethod); - break; - case "Sum": - case "Min": - case "Max": - case "Avg": - case "ToList": - case "ToOne": - case "First": - //解析:string.Join(",", w.Roles.AsSelect().ToList(b => b.RoleName) - var exp3Args0 = (exp3.Arguments[0] as UnaryExpression)?.Operand as LambdaExpression; - manySubSelectAggMethod = _dicSelectMethodToSql.GetOrAdd(fsqlType, fsqlType2 => - fsqlType2.GetMethods().Where(a => a.Name == "ToSql" && a.GetParameters().Length == 2 && a.GetParameters()[1].ParameterType == typeof(FieldAliasOptions) && a.GetGenericArguments().Length == 1).FirstOrDefault()); - if (manySubSelectAggMethod == null || exp3Args0 == null) throw new ArgumentException(CoreStrings.ManyToMany_AsSelect_NotSupport_Sum_Avg_etc); - manySubSelectAggMethod = manySubSelectAggMethod.MakeGenericMethod(exp3Args0.ReturnType); - var fsqls0p = fsql as Select0Provider; - var fsqls0pWhere = fsqls0p._where.ToString(); - fsqls0p._where.Clear(); - var fsqltablesLast = new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.InnerJoin }; - fsqltables.Add(fsqltablesLast); - fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); - fsqltablesLast.NavigateCondition = fsqls0p._where.ToString(); - if (fsqltablesLast.NavigateCondition.StartsWith(" AND (")) fsqltablesLast.NavigateCondition = fsqltablesLast.NavigateCondition.Substring(6, fsqltablesLast.NavigateCondition.Length - 7); - fsqls0p._where.Clear().Append(fsqls0pWhere); - var tsc3 = tsc.CloneDisableDiyParse(); - tsc3._tables = tsc._tables.ToList(); - var where2 = ExpressionLambdaToSql(Expression.Lambda(manySubSelectWhereExp, manySubSelectWhereParam), tsc3); - if (string.IsNullOrEmpty(where2) == false) fsqls0p._where.Append(" AND (").Append(where2).Append(")"); - - switch (exp3.Method.Name) - { - case "Sum": - case "Min": - case "Max": - case "Avg": - var map = new ReadAnonymousTypeInfo(); - var field = new StringBuilder(); - var index = -1; - - for (var a = 0; a < exp3Args0.Parameters.Count; a++) fsqls0p._tables[a].Parameter = exp3Args0.Parameters[a]; - ReadAnonymousField(fsqls0p._tables, field, map, ref index, exp3Args0, null, null, null, null, null, false); - var fieldSql = field.Length > 0 ? field.Remove(0, 2).ToString() : null; - - var sql4 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { $"{exp3.Method.Name.ToLower()}({fieldSql})" })?.ToString(); - asSelectBefores.Clear(); - return _common.IsNull($"({sql4.Replace(" \r\n", " \r\n ")})", formatSql(exp3.Method.ReturnType.CreateInstanceGetDefaultValue(), exp3.Method.ReturnType, null, null)); - } - - var sql3 = manySubSelectAggMethod.Invoke(fsql, new object[] { exp3Args0, FieldAliasOptions.AsProperty }) as string; - asSelectBefores.Clear(); - return $"({sql3.Replace(" \r\n", " \r\n ")})"; - } - asSelectBefores.Clear(); - return ExpressionLambdaToSql(manySubSelectExpBoy, tsc); - } - for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) - { - var col1 = parm123Ref.RefColumns[mn]; - var col2 = parm123Ref.Columns[mn]; - var pexp1 = Expression.Property(fsqlWhereParam, col1.CsName); - var pexp2 = Expression.Property(asSelectParentExp, col2.CsName); - if (col1.CsType != col2.CsType) - { - if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); - if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); + if (mn == 0) fsqlWhereExp = tmpExp; + else fsqlWhereExp = Expression.AndAlso(fsqlWhereExp, tmpExp); } - var tmpExp = Expression.Equal(pexp1, pexp2); - if (mn == 0) fsqlWhereExp = tmpExp; - else fsqlWhereExp = Expression.AndAlso(fsqlWhereExp, tmpExp); + fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlWhereExp, fsqlWhereParam) }); } - fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlWhereExp, fsqlWhereParam) }); } } asSelectBefores.Clear(); From d412b91233e6c1ed8c11ea97fcb6fa600444d005 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 15:17:46 +0800 Subject: [PATCH 06/20] add PgArrayToMany LazyLoding test #1145 --- FreeSql.DbContext/FreeSql.DbContext.xml | 9 -- .../PostgreSQLPgArrayToManyTest .cs | 120 ++++++++++++++++++ FreeSql/Internal/CommonExpression.cs | 2 +- FreeSql/Internal/UtilsExpressionTree.cs | 20 ++- 4 files changed, 134 insertions(+), 17 deletions(-) create mode 100644 FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 1ab5bf1cf..6b638cad4 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -795,14 +795,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs new file mode 100644 index 000000000..4ea569570 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs @@ -0,0 +1,120 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.PostgreSQL +{ + public class PostgreSQLPgArrayToManyTest + { + + [Table(Name = "pgarray_tomany_user_lazyloading")] + public class UserLazyLoading + { + public int Id { get; set; } + public int[] RoleIds { get; set; } + public string UserName { get; set; } + + [Navigate(nameof(RoleIds))] + public virtual List Roles { get; set; } + } + + [Table(Name = "pgarray_tomany_role_lazyloading")] + public class RoleLazyLoading + { + public int Id { get; set; } + public string RoleName { get; set; } + + [Navigate(nameof(UserLazyLoading.RoleIds))] + public virtual List Users { get; set; } + } + + [Fact] + public void LazyLoading() + { + var fsql = g.pgsql; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var roles = new[] + { + new RoleLazyLoading { Id = 1, RoleName = "role1" }, + new RoleLazyLoading { Id = 2, RoleName = "role2" }, + new RoleLazyLoading { Id = 3, RoleName = "role3" } + }; + Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); + var users = new[] + { + new UserLazyLoading { Id = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new UserLazyLoading { Id = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new UserLazyLoading { Id = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new UserLazyLoading { Id = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new UserLazyLoading { Id = 15, RoleIds = null, UserName = "user5" }, + new UserLazyLoading { Id = 16, RoleIds = new int[0], UserName = "user6" }, + }; + Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + + var role = fsql.Select().Where(a => a.Id == 1).First(); + Assert.IsNotType(role); + + var users2 = role.Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].Id); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].Id); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].Id); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].Id); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + var roles2 = users2[0].Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].Id); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].Id); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users2[1].Roles; + Assert.Equal(3, roles2.Count); + Assert.Equal(1, roles2[0].Id); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].Id); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].Id); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users2[2].Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].Id); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].Id); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users2[3].Roles; + Assert.Equal(3, roles2.Count); + Assert.Equal(3, roles2[0].Id); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].Id); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].Id); + Assert.Equal("role1", roles2[2].RoleName); + + var user = fsql.Select().Where(a => a.Id == 11).First(); + Assert.IsNotType(user); + + roles2 = user.Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].Id); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].Id); + Assert.Equal("role2", roles2[1].RoleName); + } + } +} diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index b7918b54e..30c34799a 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -2201,7 +2201,7 @@ void LocalSetSelectProviderAlias(string alias) midSelect.Where($"{midSelect._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.MiddleColumns[memberTbref.Columns.Count + tidx].Attribute.Name)} = {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[tidx].Attribute.Name)}"); for (var tidx = 0; tidx < memberTbref.Columns.Count; tidx++) midSelect.Where($"{midSelect._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.MiddleColumns[tidx].Attribute.Name)} = {mtmReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[tidx].Attribute.Name)}"); - select.Where($"exists({midSelect.ToSql("1").Replace(" \r\n", " \r\n ")})"); + select._where.Append($" AND exists({midSelect.ToSql("1").Replace(" \r\n", " \r\n ")})"); break; case TableRefType.OneToMany: var omtReftbname = e.FreeParse(Expression.MakeMemberAccess(memberExp.Expression, exp3Tb.Properties[exp3Tb.ColumnsByPosition[0].CsName])); diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 7e852d3be..5262560dc 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -969,9 +969,10 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo .Append(" if (base.").Append(pnv.Name).Append(" == null && __lazy__").Append(pnv.Name).AppendLine(" == false) {"); if (nvref.Exception == null) - cscode.Append(" base.").Append(pnv.Name).Append(" = __fsql_orm__.Select<").Append(propElementType.DisplayCsharp()) + cscode.Append(" var loc2 = __fsql_orm__.Select<").Append(propElementType.DisplayCsharp()) .Append(">().Where(a => __fsql_orm__.Select<").Append(tbmid.Type.DisplayCsharp()) .Append(">().Where(b => ").Append(lmbdWhere.ToString()).AppendLine(").Any()).ToList();") + .Append(" base.").Append(pnv.Name).Append(" = ").AppendLine(propTypeIsObservableCollection ? $"new ObservableCollection<{propElementType.DisplayCsharp()}>(loc2);" : "loc2;") .Append(" __lazy__").Append(pnv.Name).AppendLine(" = true;"); else cscode.Append(" throw new Exception(\"").Append(nvref.Exception.Message.Replace("\r\n", "\\r\\n").Replace("\"", "\\\"")).AppendLine("\");"); @@ -994,7 +995,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo { var isArrayToMany = false; var lmbdWhere = isLazy ? new StringBuilder() : null; - var cscodeExtLogic = ""; + var cscodeExtLogic1 = ""; + var cscodeExtLogic2 = ""; //Pgsql Array[] To Many if (common._orm.Ado.DataType == DataType.PostgreSQL) { @@ -1042,7 +1044,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - cscodeExtLogic = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; + cscodeExtLogic1 = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; + cscodeExtLogic2 = $" loc2 = this.{trycol.CsName}.Select(a => loc2.FirstOrDefault(b => b.{tbref.Primarys[0].CsName} == a)).ToList();"; lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName); if (trycol.CsType.GetElementType().IsNullableType() == false && tbref.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); lmbdWhere.Append(")"); @@ -1092,7 +1095,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) { lmbdWhere.Append(".Value"); - cscodeExtLogic = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + cscodeExtLogic1 = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; } lmbdWhere.Append(")"); nvref.Columns.Add(tbref.Primarys[0]); @@ -1219,12 +1222,14 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo if (vp?.Item2 == true) { //get 重写 cscode.Append(" ").Append(propGetModification).Append(" get {\r\n") - .Append(cscodeExtLogic) + .Append(cscodeExtLogic1) .Append(" if (base.").Append(pnv.Name).Append(" == null && __lazy__").Append(pnv.Name).AppendLine(" == false) {"); if (nvref.Exception == null) { - cscode.Append(" base.").Append(pnv.Name).Append(" = __fsql_orm__.Select<").Append(propElementType.DisplayCsharp()).Append(">().Where(a => ").Append(lmbdWhere.ToString()).AppendLine(").ToList();"); + cscode.Append(" var loc2 = __fsql_orm__.Select<").Append(propElementType.DisplayCsharp()).Append(">().Where(a => ").Append(lmbdWhere.ToString()).AppendLine(").ToList();") + .Append(cscodeExtLogic2) + .Append(" base.").Append(pnv.Name).Append(" = ").AppendLine(propTypeIsObservableCollection ? $"new ObservableCollection<{propElementType.DisplayCsharp()}>(loc2);" : "loc2;"); if (refprop != null) { cscode.Append(" foreach (var loc1 in base.").Append(pnv.Name).AppendLine(")") @@ -1381,7 +1386,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo .Append(" if (base.").Append(pnv.Name).Append(" == null && __lazy__").Append(pnv.Name).AppendLine(" == false) {"); if (nvref.Exception == null) - cscode.Append(" base.").Append(pnv.Name).Append(" = __fsql_orm__.Select<").Append(propTypeName).Append(">().Where(a => ").Append(lmbdWhere.ToString()).AppendLine(").ToOne();") + cscode.Append(" var loc3 = __fsql_orm__.Select<").Append(propTypeName).Append(">().Where(a => ").Append(lmbdWhere.ToString()).AppendLine(").ToOne();") + .Append(" base.").Append(pnv.Name).AppendLine(" = loc3;") .Append(" __lazy__").Append(pnv.Name).AppendLine(" = true;"); else cscode.Append(" throw new Exception(\"").Append(nvref.Exception.Message.Replace("\r\n", "\\r\\n").Replace("\"", "\\\"")).AppendLine("\");"); From 375f123365bd460dbbb45d8b7dae9281391c8410 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 16:09:38 +0800 Subject: [PATCH 07/20] add PgArrayToMany IncludeMany test #1147 --- FreeSql.DbContext/FreeSql.DbContext.xml | 9 ++ .../PostgreSQLPgArrayToManyTest .cs | 113 +++++++++++++----- FreeSql/Internal/UtilsExpressionTree.cs | 14 +-- 3 files changed, 98 insertions(+), 38 deletions(-) diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 6b638cad4..1ab5bf1cf 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -795,5 +795,14 @@ + + + 批量注入 Repository,可以参考代码自行调整 + + + + + + diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs index 4ea569570..8c16b41b6 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs @@ -13,7 +13,8 @@ public class PostgreSQLPgArrayToManyTest [Table(Name = "pgarray_tomany_user_lazyloading")] public class UserLazyLoading { - public int Id { get; set; } + [Column(IsPrimary = true)] + public int UserId { get; set; } public int[] RoleIds { get; set; } public string UserName { get; set; } @@ -24,7 +25,8 @@ public class UserLazyLoading [Table(Name = "pgarray_tomany_role_lazyloading")] public class RoleLazyLoading { - public int Id { get; set; } + [Column(IsPrimary = true)] + public int RoleId { get; set; } public string RoleName { get; set; } [Navigate(nameof(UserLazyLoading.RoleIds))] @@ -40,80 +42,129 @@ public void LazyLoading() var roles = new[] { - new RoleLazyLoading { Id = 1, RoleName = "role1" }, - new RoleLazyLoading { Id = 2, RoleName = "role2" }, - new RoleLazyLoading { Id = 3, RoleName = "role3" } + new RoleLazyLoading { RoleId = 1, RoleName = "role1" }, + new RoleLazyLoading { RoleId = 2, RoleName = "role2" }, + new RoleLazyLoading { RoleId = 3, RoleName = "role3" } }; Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); var users = new[] { - new UserLazyLoading { Id = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, - new UserLazyLoading { Id = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, - new UserLazyLoading { Id = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, - new UserLazyLoading { Id = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, - new UserLazyLoading { Id = 15, RoleIds = null, UserName = "user5" }, - new UserLazyLoading { Id = 16, RoleIds = new int[0], UserName = "user6" }, + new UserLazyLoading { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new UserLazyLoading { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new UserLazyLoading { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new UserLazyLoading { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new UserLazyLoading { UserId = 15, RoleIds = null, UserName = "user5" }, + new UserLazyLoading { UserId = 16, RoleIds = new int[0], UserName = "user6" }, }; Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); - var role = fsql.Select().Where(a => a.Id == 1).First(); + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + Assert.Equal(3, roles3.Count); + var roles2 = roles3; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + var users2 = roles2[0].Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + users2 = roles2[1].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + users2 = roles2[2].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(12, users2[0].UserId); + Assert.Equal("user2", users2[0].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[0].RoleIds)); + Assert.Equal(13, users2[1].UserId); + Assert.Equal("user3", users2[1].UserName); + Assert.Equal("1,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + var role = fsql.Select().Where(a => a.RoleId == 1).First(); Assert.IsNotType(role); - var users2 = role.Users; + users2 = role.Users; Assert.Equal(4, users2.Count); - Assert.Equal(11, users2[0].Id); + Assert.Equal(11, users2[0].UserId); Assert.Equal("user1", users2[0].UserName); Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); - Assert.Equal(12, users2[1].Id); + Assert.Equal(12, users2[1].UserId); Assert.Equal("user2", users2[1].UserName); Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); - Assert.Equal(13, users2[2].Id); + Assert.Equal(13, users2[2].UserId); Assert.Equal("user3", users2[2].UserName); Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); - Assert.Equal(14, users2[3].Id); + Assert.Equal(14, users2[3].UserId); Assert.Equal("user4", users2[3].UserName); Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); - var roles2 = users2[0].Roles; + roles2 = users2[0].Roles; Assert.Equal(2, roles2.Count); - Assert.Equal(1, roles2[0].Id); + Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); - Assert.Equal(2, roles2[1].Id); + Assert.Equal(2, roles2[1].RoleId); Assert.Equal("role2", roles2[1].RoleName); roles2 = users2[1].Roles; Assert.Equal(3, roles2.Count); - Assert.Equal(1, roles2[0].Id); + Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); - Assert.Equal(2, roles2[1].Id); + Assert.Equal(2, roles2[1].RoleId); Assert.Equal("role2", roles2[1].RoleName); - Assert.Equal(3, roles2[2].Id); + Assert.Equal(3, roles2[2].RoleId); Assert.Equal("role3", roles2[2].RoleName); roles2 = users2[2].Roles; Assert.Equal(2, roles2.Count); - Assert.Equal(1, roles2[0].Id); + Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); - Assert.Equal(3, roles2[1].Id); + Assert.Equal(3, roles2[1].RoleId); Assert.Equal("role3", roles2[1].RoleName); roles2 = users2[3].Roles; Assert.Equal(3, roles2.Count); - Assert.Equal(3, roles2[0].Id); + Assert.Equal(3, roles2[0].RoleId); Assert.Equal("role3", roles2[0].RoleName); - Assert.Equal(2, roles2[1].Id); + Assert.Equal(2, roles2[1].RoleId); Assert.Equal("role2", roles2[1].RoleName); - Assert.Equal(1, roles2[2].Id); + Assert.Equal(1, roles2[2].RoleId); Assert.Equal("role1", roles2[2].RoleName); - var user = fsql.Select().Where(a => a.Id == 11).First(); + var user = fsql.Select().Where(a => a.UserId == 11).First(); Assert.IsNotType(user); roles2 = user.Roles; Assert.Equal(2, roles2.Count); - Assert.Equal(1, roles2[0].Id); + Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); - Assert.Equal(2, roles2[1].Id); + Assert.Equal(2, roles2[1].RoleId); Assert.Equal("role2", roles2[1].RoleName); } } diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 5262560dc..35fff5825 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -1028,13 +1028,13 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo if (pnvBind == null && trycol == null) { var findtbrefPkCsName = tbref.Primarys[0].CsName.TrimStart('_'); - if (findtbrefPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtbrefPkCsName = findtbrefPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); + if (findtbrefPkCsName.StartsWith(tbref.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtbrefPkCsName = findtbrefPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); var findtrytb = pnv.Name; if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); findtrytb += tbref.CsName; if ( - tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtbrefPkCsName}", out trycol) == false && //骆峰命名 - tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtbrefPkCsName}", out trycol) == false //下划线命名 + trytb.ColumnsByCs.TryGetValue($"{findtrytb}{findtbrefPkCsName}s", out trycol) == false && //骆峰命名 + trytb.ColumnsByCs.TryGetValue($"{findtrytb}_{findtbrefPkCsName}s", out trycol) == false //下划线命名 ) { } @@ -1077,11 +1077,11 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo var findtrytbPkCsName = trytb.Primarys[0].CsName.TrimStart('_'); if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); var findtrytb = pnv.Name; - if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); + if (findtrytb.EndsWith($"{trytb.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - trytb.CsName.Length - 1); findtrytb += trytb.CsName; if ( - tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 - tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}", out trycol) == false //下划线命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}s", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}s", out trycol) == false //下划线命名 ) { } @@ -1098,7 +1098,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo cscodeExtLogic1 = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; } lmbdWhere.Append(")"); - nvref.Columns.Add(tbref.Primarys[0]); + nvref.Columns.Add(trytb.Primarys[0]); nvref.RefColumns.Add(trycol); nvref.RefEntityType = tbref.Type; nvref.RefType = TableRefType.PgArrayToMany; From 18215aa44151b862f5e154054f7508ed694c3a30 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 16:22:49 +0800 Subject: [PATCH 08/20] add PgArrayToMany IncludeMany test2 #1147 --- .../PostgreSQLPgArrayToManyTest .cs | 57 ++++++++++++++++++- .../SelectProvider/Select1Provider.cs | 6 +- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs index 8c16b41b6..fac2fbfbe 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs @@ -58,9 +58,62 @@ public void LazyLoading() }; Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + Assert.Equal(6, users3.Count); + var users2 = users3; + Assert.Equal(11, users2[0].UserId); + Assert.Equal(12, users2[1].UserId); + Assert.Equal(13, users2[2].UserId); + Assert.Equal(14, users2[3].UserId); + Assert.Equal(15, users2[4].UserId); + Assert.Equal(16, users2[5].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("user5", users2[4].UserName); + Assert.Equal("user6", users2[5].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + Assert.Null(users2[4].RoleIds); + Assert.Empty(users2[5].RoleIds); + + var roles2 = users3[0].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users3[1].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users3[2].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users3[3].Roles; + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + Assert.Null(users3[4].Roles); + Assert.Empty(users3[5].Roles); + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); Assert.Equal(3, roles3.Count); - var roles2 = roles3; + roles2 = roles3; Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); Assert.Equal(2, roles2[1].RoleId); @@ -68,7 +121,7 @@ public void LazyLoading() Assert.Equal(3, roles2[2].RoleId); Assert.Equal("role3", roles2[2].RoleName); - var users2 = roles2[0].Users; + users2 = roles2[0].Users; Assert.Equal(4, users2.Count); Assert.Equal(11, users2[0].UserId); Assert.Equal("user1", users2[0].UserName); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 838e01ebf..348e21ebf 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -1184,11 +1184,12 @@ public ISelect IncludeMany(Expression { var arrVal = getListValue(a, tbref.Columns[0].CsName, 0) as Array; + if (arrVal == null) return null; var arrObjVal = new object[arrVal.Length]; arrVal.CopyTo(arrObjVal, 0); return arrObjVal; - }).Where(a => a != null).ToArray(); - var arrExp = Expression.NewArrayInit(tbref.RefColumns[0].CsType, listKeys.SelectMany(a => a).Distinct() + }).ToArray(); + var arrExp = Expression.NewArrayInit(tbref.RefColumns[0].CsType, listKeys.Where(a => a != null).SelectMany(a => a).Distinct() .Select(a => Expression.Constant(Utils.GetDataReaderValue(tbref.RefColumns[0].CsType, a), tbref.RefColumns[0].CsType)).ToArray()); var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); var containsMethod = _dicTypeMethod.GetOrAdd(tbref.RefColumns[0].CsType, et => new ConcurrentDictionary()).GetOrAdd("Contains", mn => @@ -1235,6 +1236,7 @@ public ISelect IncludeMany(Expression(); foreach (var dlk in dicListKeys) { From e09282350170274a3f6e281f78ed10145367f472 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 19:20:59 +0800 Subject: [PATCH 09/20] add PgArrayToMany IncludeMany Dto test #1147 --- .../PostgreSQLPgArrayToManyTest .cs | 11 ++ FreeSql/FreeSql.xml | 183 ------------------ .../SelectProvider/Select1Provider.cs | 11 ++ FreeSql/Internal/Model/TableInfo.cs | 3 +- 4 files changed, 24 insertions(+), 184 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs index fac2fbfbe..2b7f32c59 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs @@ -58,6 +58,17 @@ public void LazyLoading() }; Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + { + user = a, roles = a.Roles + }); + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + { + role = a, + users = a.Users + }); + + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); Assert.Equal(6, users3.Count); var users2 = users3; diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 434ec7a25..160a7397d 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -3303,177 +3303,6 @@ - - - 测试数据库是否连接正确,本方法执行如下命令: - MySql/SqlServer/PostgreSQL/达梦/人大金仓/神通: SELECT 1 - Oracle: SELECT 1 FROM dual - - 命令超时设置(秒) - - true: 成功, false: 失败 - - - - 查询,若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】 - - - - - - - - - - 查询,ExecuteReaderAsync(dr => {}, "select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 查询 - - - - - - - - - 查询,ExecuteArrayAsync("select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 查询 - - - - - - - - - 查询,ExecuteDataSetAsync("select * from user where age > @age; select 2", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 查询 - - - - - - - - - 查询,ExecuteDataTableAsync("select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 在【主库】执行 - - - - - - - - - 在【主库】执行,ExecuteNonQueryAsync("delete from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 在【主库】执行 - - - - - - - - - 在【主库】执行,ExecuteScalarAsync("select 1 from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new SqlParameter { ParameterName = "age", Value = 25 }) - - - - - - - - - - - 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - - 执行SQL返回对象集合,Query<User>("select * from user where age > @age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 }) - - - - - - - - - - - - 执行SQL返回对象集合,Query<User, Address>("select * from user where age > @age; select * from address", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - 可自定义解析表达式 @@ -4497,12 +4326,6 @@ 超时 - - - 获取资源 - - - 使用完毕后,归还资源 @@ -4578,12 +4401,6 @@ 资源对象 - - - 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 - - 资源对象 - 归还对象给对象池的时候触发 diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 348e21ebf..7f8a81d49 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -222,6 +222,17 @@ MemberInitExpression GetIncludeManyNewInitExpression(IncludeManyNewInit imni) var bindings = new List(); if (imni.IsOutputPrimary) bindings.AddRange(imni.Table.Primarys.Select(a => Expression.Bind(imni.Table.Properties[a.CsName], Expression.MakeMemberAccess(imni.CurrentExpression, imni.Table.Properties[a.CsName])))); if (imni.Childs.Any()) bindings.AddRange(imni.Childs.Select(a => Expression.Bind(imni.Table.Properties[a.Key], GetIncludeManyNewInitExpression(a.Value)))); + var pgarrayToManys = imni.Table.GetAllTableRef().Select(tr => + { + if (tr.Value.RefType != TableRefType.PgArrayToMany) return null; + var reftb = _orm.CodeFirst.GetTableByEntity(tr.Value.RefEntityType); + if (tr.Value.RefColumns[0] == reftb.Primarys[0]) + { + bindings.Add(Expression.Bind(imni.Table.Properties[tr.Value.Columns[0].CsName], Expression.MakeMemberAccess(imni.CurrentExpression, imni.Table.Properties[tr.Value.Columns[0].CsName]))); + return tr.Key; + } + return null; + }).ToList(); return Expression.MemberInit(imni.Table.Type.InternalNewExpression(), bindings); } diff --git a/FreeSql/Internal/Model/TableInfo.cs b/FreeSql/Internal/Model/TableInfo.cs index 6c8583149..13555dfd2 100644 --- a/FreeSql/Internal/Model/TableInfo.cs +++ b/FreeSql/Internal/Model/TableInfo.cs @@ -48,6 +48,7 @@ public TableRef GetTableRef(string propertyName, bool isThrowException) } return tryref; } + public IEnumerable> GetAllTableRef() => _refs; //public void CopyTo(TableInfo target) //{ @@ -79,7 +80,7 @@ public TableRef GetTableRef(string propertyName, bool isThrowException) // target.VersionColumn = getOrCloneColumn(this.VersionColumn); // foreach (var rf in this._refs) target._refs.TryAdd(rf.Key, new TableRef // { - + // }); From e900f8383ab882e480039bba6717d7fb046a1acc Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 21:09:43 +0800 Subject: [PATCH 10/20] add PgArrayToMany ObservableCollection test #1145 --- FreeSql.DbContext/FreeSql.DbContext.xml | 9 - .../LazyLoadingTest .cs} | 92 ++++-- .../PostgreSQL/PgArrayToMany/NormalTest.cs | 221 ++++++++++++++ .../ObservableCollectionLazyLoadingTest.cs | 275 ++++++++++++++++++ .../PgArrayToMany/ObservableCollectionTest.cs | 223 ++++++++++++++ FreeSql/FreeSql.xml | 183 ++++++++++++ FreeSql/Internal/CommonExpression.cs | 32 +- FreeSql/Internal/UtilsExpressionTree.cs | 26 +- 8 files changed, 1010 insertions(+), 51 deletions(-) rename FreeSql.Tests/FreeSql.Tests/PostgreSQL/{PostgreSQLPgArrayToManyTest .cs => PgArrayToMany/LazyLoadingTest .cs} (68%) create mode 100644 FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/NormalTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionLazyLoadingTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionTest.cs diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 1ab5bf1cf..6b638cad4 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -795,14 +795,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/LazyLoadingTest .cs similarity index 68% rename from FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs rename to FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/LazyLoadingTest .cs index 2b7f32c59..834d83b8b 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/LazyLoadingTest .cs @@ -1,17 +1,18 @@ using FreeSql.DataAnnotations; using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Text; using Xunit; -namespace FreeSql.Tests.PostgreSQL +namespace FreeSql.Tests.PostgreSQL.PgArrayToMany { - public class PostgreSQLPgArrayToManyTest + public class LazyLoadingTest { [Table(Name = "pgarray_tomany_user_lazyloading")] - public class UserLazyLoading + public class User { [Column(IsPrimary = true)] public int UserId { get; set; } @@ -19,57 +20,96 @@ public class UserLazyLoading public string UserName { get; set; } [Navigate(nameof(RoleIds))] - public virtual List Roles { get; set; } + public virtual List Roles { get; set; } } [Table(Name = "pgarray_tomany_role_lazyloading")] - public class RoleLazyLoading + public class Role { [Column(IsPrimary = true)] public int RoleId { get; set; } public string RoleName { get; set; } - [Navigate(nameof(UserLazyLoading.RoleIds))] - public virtual List Users { get; set; } + [Navigate(nameof(User.RoleIds))] + public virtual List Users { get; set; } } [Fact] public void LazyLoading() { var fsql = g.pgsql; - fsql.Delete().Where("1=1").ExecuteAffrows(); - fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); var roles = new[] { - new RoleLazyLoading { RoleId = 1, RoleName = "role1" }, - new RoleLazyLoading { RoleId = 2, RoleName = "role2" }, - new RoleLazyLoading { RoleId = 3, RoleName = "role3" } + new Role { RoleId = 1, RoleName = "role1" }, + new Role { RoleId = 2, RoleName = "role2" }, + new Role { RoleId = 3, RoleName = "role3" } }; Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); var users = new[] { - new UserLazyLoading { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, - new UserLazyLoading { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, - new UserLazyLoading { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, - new UserLazyLoading { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, - new UserLazyLoading { UserId = 15, RoleIds = null, UserName = "user5" }, - new UserLazyLoading { UserId = 16, RoleIds = new int[0], UserName = "user6" }, + new User { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new User { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new User { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new User { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new User { UserId = 15, RoleIds = null, UserName = "user5" }, + new User { UserId = 16, RoleIds = new int[0], UserName = "user6" }, }; Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); - var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + var users5Select = fsql.Select().Where(a => a.Roles.Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_lazyloading"" b + WHERE (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) AND (b.""rolename"" = 'role1') + limit 1))", users5Select.ToSql()); + var users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + users5Select = fsql.Select().Where(a => a.Roles.AsSelect().Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_lazyloading"" b + WHERE (b.""rolename"" = 'role1') AND (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) + limit 1))", users5Select.ToSql()); + users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + + var roles5Select = fsql.Select().Where(a => a.Users.Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_lazyloading"" b + WHERE (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) AND (b.""username"" = 'user1') + limit 1))", roles5Select.ToSql()); + var roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + roles5Select = fsql.Select().Where(a => a.Users.AsSelect().Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_lazyloading"" b + WHERE (b.""username"" = 'user1') AND (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) + limit 1))", roles5Select.ToSql()); + roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + + + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new { user = a, roles = a.Roles }); - var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new { role = a, users = a.Users }); - var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); Assert.Equal(6, users3.Count); var users2 = users3; Assert.Equal(11, users2[0].UserId); @@ -122,7 +162,7 @@ public void LazyLoading() Assert.Null(users3[4].Roles); Assert.Empty(users3[5].Roles); - var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); Assert.Equal(3, roles3.Count); roles2 = roles3; Assert.Equal(1, roles2[0].RoleId); @@ -171,8 +211,8 @@ public void LazyLoading() Assert.Equal("user4", users2[2].UserName); Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); - var role = fsql.Select().Where(a => a.RoleId == 1).First(); - Assert.IsNotType(role); + var role = fsql.Select().Where(a => a.RoleId == 1).First(); + Assert.IsNotType(role); users2 = role.Users; Assert.Equal(4, users2.Count); @@ -221,8 +261,8 @@ public void LazyLoading() Assert.Equal(1, roles2[2].RoleId); Assert.Equal("role1", roles2[2].RoleName); - var user = fsql.Select().Where(a => a.UserId == 11).First(); - Assert.IsNotType(user); + var user = fsql.Select().Where(a => a.UserId == 11).First(); + Assert.IsNotType(user); roles2 = user.Roles; Assert.Equal(2, roles2.Count); diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/NormalTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/NormalTest.cs new file mode 100644 index 000000000..a697dc625 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/NormalTest.cs @@ -0,0 +1,221 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.PostgreSQL.PgArrayToMany +{ + public class NormalTest + { + + [Table(Name = "pgarray_tomany_user_normal")] + public class User + { + [Column(IsPrimary = true)] + public int UserId { get; set; } + public int[] RoleIds { get; set; } + public string UserName { get; set; } + + [Navigate(nameof(RoleIds))] + public List Roles { get; set; } + } + + [Table(Name = "pgarray_tomany_role_normal")] + public class Role + { + [Column(IsPrimary = true)] + public int RoleId { get; set; } + public string RoleName { get; set; } + + [Navigate(nameof(User.RoleIds))] + public List Users { get; set; } + } + + [Fact] + public void Normal() + { + var fsql = g.pgsql; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var roles = new[] + { + new Role { RoleId = 1, RoleName = "role1" }, + new Role { RoleId = 2, RoleName = "role2" }, + new Role { RoleId = 3, RoleName = "role3" } + }; + Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); + var users = new[] + { + new User { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new User { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new User { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new User { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new User { UserId = 15, RoleIds = null, UserName = "user5" }, + new User { UserId = 16, RoleIds = new int[0], UserName = "user6" }, + }; + Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + + var users5Select = fsql.Select().Where(a => a.Roles.Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_normal"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_normal"" b + WHERE (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) AND (b.""rolename"" = 'role1') + limit 1))", users5Select.ToSql()); + var users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + users5Select = fsql.Select().Where(a => a.Roles.AsSelect().Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_normal"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_normal"" b + WHERE (b.""rolename"" = 'role1') AND (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) + limit 1))", users5Select.ToSql()); + users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + + var roles5Select = fsql.Select().Where(a => a.Users.Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_normal"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_normal"" b + WHERE (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) AND (b.""username"" = 'user1') + limit 1))", roles5Select.ToSql()); + var roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + roles5Select = fsql.Select().Where(a => a.Users.AsSelect().Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_normal"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_normal"" b + WHERE (b.""username"" = 'user1') AND (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) + limit 1))", roles5Select.ToSql()); + roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + + + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + { + user = a, roles = a.Roles + }); + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + { + role = a, + users = a.Users + }); + + + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + Assert.Equal(6, users3.Count); + var users2 = users3; + Assert.Equal(11, users2[0].UserId); + Assert.Equal(12, users2[1].UserId); + Assert.Equal(13, users2[2].UserId); + Assert.Equal(14, users2[3].UserId); + Assert.Equal(15, users2[4].UserId); + Assert.Equal(16, users2[5].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("user5", users2[4].UserName); + Assert.Equal("user6", users2[5].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + Assert.Null(users2[4].RoleIds); + Assert.Empty(users2[5].RoleIds); + + var roles2 = users3[0].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users3[1].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users3[2].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users3[3].Roles; + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + Assert.Null(users3[4].Roles); + Assert.Empty(users3[5].Roles); + + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + Assert.Equal(3, roles3.Count); + roles2 = roles3; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + users2 = roles2[0].Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + users2 = roles2[1].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + users2 = roles2[2].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(12, users2[0].UserId); + Assert.Equal("user2", users2[0].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[0].RoleIds)); + Assert.Equal(13, users2[1].UserId); + Assert.Equal("user3", users2[1].UserName); + Assert.Equal("1,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + var role = fsql.Select().Where(a => a.RoleId == 1).First(); + Assert.IsType(role); + + var user = fsql.Select().Where(a => a.UserId == 11).First(); + Assert.IsType(user); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionLazyLoadingTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionLazyLoadingTest.cs new file mode 100644 index 000000000..29d9d271b --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionLazyLoadingTest.cs @@ -0,0 +1,275 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.PostgreSQL.PgArrayToMany +{ + public class ObservableCollectionLazyLoadingTest + { + + [Table(Name = "pgarray_tomany_user_observablecollection_lazyloading")] + public class User + { + [Column(IsPrimary = true)] + public int UserId { get; set; } + public int[] RoleIds { get; set; } + public string UserName { get; set; } + + [Navigate(nameof(RoleIds))] + public virtual List Roles { get; set; } + } + + [Table(Name = "pgarray_tomany_role_observablecollection_lazyloading")] + public class Role + { + [Column(IsPrimary = true)] + public int RoleId { get; set; } + public string RoleName { get; set; } + + [Navigate(nameof(User.RoleIds))] + public virtual List Users { get; set; } + } + + [Fact] + public void ObservableCollectionLazyLoading() + { + var fsql = g.pgsql; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var roles = new[] + { + new Role { RoleId = 1, RoleName = "role1" }, + new Role { RoleId = 2, RoleName = "role2" }, + new Role { RoleId = 3, RoleName = "role3" } + }; + Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); + var users = new[] + { + new User { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new User { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new User { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new User { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new User { UserId = 15, RoleIds = null, UserName = "user5" }, + new User { UserId = 16, RoleIds = new int[0], UserName = "user6" }, + }; + Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + + var users5Select = fsql.Select().Where(a => a.Roles.Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_observablecollection_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_observablecollection_lazyloading"" b + WHERE (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) AND (b.""rolename"" = 'role1') + limit 1))", users5Select.ToSql()); + var users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + users5Select = fsql.Select().Where(a => a.Roles.AsSelect().Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_observablecollection_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_observablecollection_lazyloading"" b + WHERE (b.""rolename"" = 'role1') AND (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) + limit 1))", users5Select.ToSql()); + users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + + var roles5Select = fsql.Select().Where(a => a.Users.Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_observablecollection_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_observablecollection_lazyloading"" b + WHERE (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) AND (b.""username"" = 'user1') + limit 1))", roles5Select.ToSql()); + var roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + roles5Select = fsql.Select().Where(a => a.Users.AsSelect().Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_observablecollection_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_observablecollection_lazyloading"" b + WHERE (b.""username"" = 'user1') AND (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) + limit 1))", roles5Select.ToSql()); + roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + + + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + { + user = a, roles = a.Roles + }); + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + { + role = a, + users = a.Users + }); + + + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + Assert.Equal(6, users3.Count); + var users2 = users3; + Assert.Equal(11, users2[0].UserId); + Assert.Equal(12, users2[1].UserId); + Assert.Equal(13, users2[2].UserId); + Assert.Equal(14, users2[3].UserId); + Assert.Equal(15, users2[4].UserId); + Assert.Equal(16, users2[5].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("user5", users2[4].UserName); + Assert.Equal("user6", users2[5].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + Assert.Null(users2[4].RoleIds); + Assert.Empty(users2[5].RoleIds); + + var roles2 = users3[0].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users3[1].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users3[2].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users3[3].Roles; + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + Assert.Null(users3[4].Roles); + Assert.Empty(users3[5].Roles); + + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + Assert.Equal(3, roles3.Count); + roles2 = roles3; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + users2 = roles2[0].Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + users2 = roles2[1].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + users2 = roles2[2].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(12, users2[0].UserId); + Assert.Equal("user2", users2[0].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[0].RoleIds)); + Assert.Equal(13, users2[1].UserId); + Assert.Equal("user3", users2[1].UserName); + Assert.Equal("1,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + var role = fsql.Select().Where(a => a.RoleId == 1).First(); + Assert.IsNotType(role); + + users2 = role.Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + roles2 = users2[0].Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users2[1].Roles; + Assert.Equal(3, roles2.Count); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users2[2].Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users2[3].Roles; + Assert.Equal(3, roles2.Count); + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + var user = fsql.Select().Where(a => a.UserId == 11).First(); + Assert.IsNotType(user); + + roles2 = user.Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionTest.cs new file mode 100644 index 000000000..671dadca4 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionTest.cs @@ -0,0 +1,223 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.PostgreSQL.PgArrayToMany +{ + public class ObservableCollectionTest + { + + [Table(Name = "pgarray_tomany_user_observablecollection")] + public class User + { + [Column(IsPrimary = true)] + public int UserId { get; set; } + public int[] RoleIds { get; set; } + public string UserName { get; set; } + + [Navigate(nameof(RoleIds))] + public ObservableCollection Roles { get; set; } + } + + [Table(Name = "pgarray_tomany_role_observablecollection")] + public class Role + { + [Column(IsPrimary = true)] + public int RoleId { get; set; } + public string RoleName { get; set; } + + [Navigate(nameof(User.RoleIds))] + public ObservableCollection Users { get; set; } + } + + [Fact] + public void ObservableCollection() + { + var fsql = g.pgsql; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var roles = new[] + { + new Role { RoleId = 1, RoleName = "role1" }, + new Role { RoleId = 2, RoleName = "role2" }, + new Role { RoleId = 3, RoleName = "role3" } + }; + Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); + var users = new[] + { + new User { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new User { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new User { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new User { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new User { UserId = 15, RoleIds = null, UserName = "user5" }, + new User { UserId = 16, RoleIds = new int[0], UserName = "user6" }, + }; + Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + + var users5Select = fsql.Select().Where(a => a.Roles.Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_observablecollection"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_observablecollection"" b + WHERE (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) AND (b.""rolename"" = 'role1') + limit 1))", users5Select.ToSql()); + var users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + users5Select = fsql.Select().Where(a => a.Roles.AsSelect().Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_observablecollection"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_observablecollection"" b + WHERE (b.""rolename"" = 'role1') AND (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) + limit 1))", users5Select.ToSql()); + users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + + var roles5Select = fsql.Select().Where(a => a.Users.Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_observablecollection"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_observablecollection"" b + WHERE (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) AND (b.""username"" = 'user1') + limit 1))", roles5Select.ToSql()); + var roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + roles5Select = fsql.Select().Where(a => a.Users.AsSelect().Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_observablecollection"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_observablecollection"" b + WHERE (b.""username"" = 'user1') AND (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) + limit 1))", roles5Select.ToSql()); + roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + + + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + { + user = a, + roles = a.Roles + }); + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + { + role = a, + users = a.Users + }); + + + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + Assert.Equal(6, users3.Count); + var users2 = users3; + Assert.Equal(11, users2[0].UserId); + Assert.Equal(12, users2[1].UserId); + Assert.Equal(13, users2[2].UserId); + Assert.Equal(14, users2[3].UserId); + Assert.Equal(15, users2[4].UserId); + Assert.Equal(16, users2[5].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("user5", users2[4].UserName); + Assert.Equal("user6", users2[5].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + Assert.Null(users2[4].RoleIds); + Assert.Empty(users2[5].RoleIds); + + var roles2 = users3[0].Roles.ToList(); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users3[1].Roles.ToList(); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users3[2].Roles.ToList(); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users3[3].Roles.ToList(); + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + Assert.Null(users3[4].Roles); + Assert.Empty(users3[5].Roles); + + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + Assert.Equal(3, roles3.Count); + roles2 = roles3.ToList(); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + users2 = roles2[0].Users.ToList(); + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + users2 = roles2[1].Users.ToList(); + Assert.Equal(3, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + users2 = roles2[2].Users.ToList(); + Assert.Equal(3, users2.Count); + Assert.Equal(12, users2[0].UserId); + Assert.Equal("user2", users2[0].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[0].RoleIds)); + Assert.Equal(13, users2[1].UserId); + Assert.Equal("user3", users2[1].UserName); + Assert.Equal("1,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + var role = fsql.Select().Where(a => a.RoleId == 1).First(); + Assert.IsType(role); + + var user = fsql.Select().Where(a => a.UserId == 11).First(); + Assert.IsType(user); + } + } +} diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 160a7397d..434ec7a25 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -3303,6 +3303,177 @@ + + + 测试数据库是否连接正确,本方法执行如下命令: + MySql/SqlServer/PostgreSQL/达梦/人大金仓/神通: SELECT 1 + Oracle: SELECT 1 FROM dual + + 命令超时设置(秒) + + true: 成功, false: 失败 + + + + 查询,若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】 + + + + + + + + + + 查询,ExecuteReaderAsync(dr => {}, "select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 查询 + + + + + + + + + 查询,ExecuteArrayAsync("select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 查询 + + + + + + + + + 查询,ExecuteDataSetAsync("select * from user where age > @age; select 2", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 查询 + + + + + + + + + 查询,ExecuteDataTableAsync("select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 在【主库】执行 + + + + + + + + + 在【主库】执行,ExecuteNonQueryAsync("delete from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 在【主库】执行 + + + + + + + + + 在【主库】执行,ExecuteScalarAsync("select 1 from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new SqlParameter { ParameterName = "age", Value = 25 }) + + + + + + + + + + + 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + + 执行SQL返回对象集合,Query<User>("select * from user where age > @age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 }) + + + + + + + + + + + + 执行SQL返回对象集合,Query<User, Address>("select * from user where age > @age; select * from address", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + 可自定义解析表达式 @@ -4326,6 +4497,12 @@ 超时 + + + 获取资源 + + + 使用完毕后,归还资源 @@ -4401,6 +4578,12 @@ 资源对象 + + + 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 + + 资源对象 + 归还对象给对象池的时候触发 diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 30c34799a..9b22e35d0 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -1212,9 +1212,19 @@ public string ExpressionLambdaToSql(Expression exp, ExpTSC tsc) var amtReftbname = ExpressionLambdaToSql(Expression.MakeMemberAccess(asSelectParentExp, parm123Tb.Properties[parm123Tb.ColumnsByPosition[0].CsName]), tsc); amtReftbname = amtReftbname.Substring(0, amtReftbname.Length - _common.QuoteSqlName(parm123Tb.ColumnsByPosition[0].Attribute.Name).Length - 1); if (parm123Ref.RefColumns[0] == fsqltables[0].Table.Primarys[0]) - (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)} @> {fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)}").Append(")"); - else if (parm123Ref.Columns[0] == fsqltables[0].Table.Primarys[0]) - (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)} @> {fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)}").Append(")"); + { + var dbinfo = _common._orm.CodeFirst.GetDbInfo(parm123Ref.Columns[0].CsType); + (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)} @> ARRAY[{fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)}]::{dbinfo?.dbtype}").Append(")"); + } + else if (parm123Ref.Columns[0] == parm123Tb.Primarys[0]) + { + var dbinfo = _common._orm.CodeFirst.GetDbInfo(parm123Ref.RefColumns[0].CsType); + (fsql as Select0Provider)._where.Append(" AND (").Append($"{fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)} @> ARRAY[{amtReftbname}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)}]::{dbinfo?.dbtype}").Append(")"); + } + else + { + ; + } } else { @@ -2213,9 +2223,19 @@ void LocalSetSelectProviderAlias(string alias) var amtReftbname = e.FreeParse(Expression.MakeMemberAccess(memberExp.Expression, exp3Tb.Properties[exp3Tb.ColumnsByPosition[0].CsName])); amtReftbname = amtReftbname.Substring(0, amtReftbname.Length - commonExp._common.QuoteSqlName(exp3Tb.ColumnsByPosition[0].Attribute.Name).Length - 1); if (memberTbref.RefColumns[0] == select._tables[0].Table.Primarys[0]) - select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)} @> {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)}"); - else if (memberTbref.Columns[0] == select._tables[0].Table.Primarys[0]) - select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)} @> {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)}"); + { + var dbinfo = commonExp._common._orm.CodeFirst.GetDbInfo(memberTbref.Columns[0].CsType); + select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)} @> ARRAY[{select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)}]::{dbinfo?.dbtype}"); + } + else if (memberTbref.Columns[0] == exp3Tb.Primarys[0]) + { + var dbinfo = commonExp._common._orm.CodeFirst.GetDbInfo(memberTbref.RefColumns[0].CsType); + select.Where($"{select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)} @> ARRAY[{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)}]::{dbinfo?.dbtype}"); + } + else + { + ; + } break; } diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 35fff5825..23250bb11 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -1044,11 +1044,14 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - cscodeExtLogic1 = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; - cscodeExtLogic2 = $" loc2 = this.{trycol.CsName}.Select(a => loc2.FirstOrDefault(b => b.{tbref.Primarys[0].CsName} == a)).ToList();"; - lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName); - if (trycol.CsType.GetElementType().IsNullableType() == false && tbref.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); - lmbdWhere.Append(")"); + if (isLazy) + { + cscodeExtLogic1 = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; + cscodeExtLogic2 = $" loc2 = this.{trycol.CsName}.Select(a => loc2.FirstOrDefault(b => b.{tbref.Primarys[0].CsName} == a)).ToList();"; + lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName); + if (trycol.CsType.GetElementType().IsNullableType() == false && tbref.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); + lmbdWhere.Append(")"); + } nvref.Columns.Add(trycol); nvref.RefColumns.Add(tbref.Primarys[0]); nvref.RefEntityType = tbref.Type; @@ -1091,13 +1094,16 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName); - if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) + if (isLazy) { - lmbdWhere.Append(".Value"); - cscodeExtLogic1 = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName); + if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) + { + lmbdWhere.Append(".Value"); + cscodeExtLogic1 = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + } + lmbdWhere.Append(")"); } - lmbdWhere.Append(")"); nvref.Columns.Add(trytb.Primarys[0]); nvref.RefColumns.Add(trycol); nvref.RefEntityType = tbref.Type; From 8ca0414500c528f32b9681f11f2464b4de886a0e Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 4 Jun 2022 00:35:33 +0800 Subject: [PATCH 11/20] init ArrayToMany --- FreeSql.DbContext/DbSet/DbSetAsync.cs | 16 +- FreeSql.DbContext/DbSet/DbSetSync.cs | 14 +- FreeSql/Internal/CommonExpression.cs | 5 + .../SelectProvider/Select1Provider.cs | 4 + FreeSql/Internal/Model/TableInfo.cs | 2 +- FreeSql/Internal/UtilsExpressionTree.cs | 265 ++++++++++++------ 6 files changed, 212 insertions(+), 94 deletions(-) diff --git a/FreeSql.DbContext/DbSet/DbSetAsync.cs b/FreeSql.DbContext/DbSet/DbSetAsync.cs index 13533430e..5bdccd0cf 100644 --- a/FreeSql.DbContext/DbSet/DbSetAsync.cs +++ b/FreeSql.DbContext/DbSet/DbSetAsync.cs @@ -155,8 +155,9 @@ async public Task SaveManyAsync(TEntity item, string propertyName, CancellationT if (tref == null) return; switch (tref.RefType) { - case Internal.Model.TableRefType.OneToOne: - case Internal.Model.TableRefType.ManyToOne: + case TableRefType.OneToOne: + case TableRefType.ManyToOne: + case TableRefType.ArrayToMany: throw new ArgumentException(DbContextStrings.PropertyOfType_IsNot_OneToManyOrManyToMany(_table.Type.FullName, propertyName)); } @@ -166,7 +167,7 @@ async public Task SaveManyAsync(TEntity item, string propertyName, CancellationT try { await AddOrUpdateNavigateAsync(item, false, propertyName, cancellationToken); - if (tref.RefType == Internal.Model.TableRefType.OneToMany) + if (tref.RefType == TableRefType.OneToMany) { await DbContextFlushCommandAsync(cancellationToken); //删除没有保存的数据,求出主体的条件 @@ -213,7 +214,7 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam DbSet refSet = null; switch (tref.RefType) { - case Internal.Model.TableRefType.OneToOne: + case TableRefType.OneToOne: refSet = GetDbSetObject(tref.RefEntityType); var propValItem = GetItemValue(item, prop); if (propValItem == null) return; @@ -225,7 +226,8 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam if (isAdd) await refSet.AddAsync(propValItem); else await refSet.AddOrUpdateAsync(propValItem); return; - case Internal.Model.TableRefType.ManyToOne: + case TableRefType.ManyToOne: + case TableRefType.ArrayToMany: return; } @@ -234,7 +236,7 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam refSet = GetDbSetObject(tref.RefEntityType); switch (tref.RefType) { - case Internal.Model.TableRefType.ManyToMany: + case TableRefType.ManyToMany: var curList = new List(); foreach (var propValItem in propValEach) { @@ -327,7 +329,7 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam await midSet.AddRangeAsync(midListAdd, cancellationToken); } break; - case Internal.Model.TableRefType.OneToMany: + case TableRefType.OneToMany: var addList = new List(); var addOrUpdateList = new List(); foreach (var propValItem in propValEach) diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index 8bafd8ad1..fc9bb1e10 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -166,8 +166,9 @@ public void SaveMany(TEntity item, string propertyName) if (tref == null) return; switch (tref.RefType) { - case Internal.Model.TableRefType.OneToOne: - case Internal.Model.TableRefType.ManyToOne: + case TableRefType.OneToOne: + case TableRefType.ManyToOne: + case TableRefType.ArrayToMany: throw new ArgumentException(DbContextStrings.PropertyOfType_IsNot_OneToManyOrManyToMany(_table.Type.FullName, propertyName)); } @@ -177,7 +178,7 @@ public void SaveMany(TEntity item, string propertyName) try { AddOrUpdateNavigate(item, false, propertyName); - if (tref.RefType == Internal.Model.TableRefType.OneToMany) + if (tref.RefType == TableRefType.OneToMany) { DbContextFlushCommand(); //删除没有保存的数据,求出主体的条件 @@ -224,7 +225,7 @@ void AddOrUpdateNavigate(TEntity item, bool isAdd, string propertyName) DbSet refSet = null; switch (tref.RefType) { - case Internal.Model.TableRefType.OneToOne: + case TableRefType.OneToOne: refSet = GetDbSetObject(tref.RefEntityType); var propValItem = GetItemValue(item, prop); if (propValItem == null) return; @@ -236,7 +237,8 @@ void AddOrUpdateNavigate(TEntity item, bool isAdd, string propertyName) if (isAdd) refSet.Add(propValItem); else refSet.AddOrUpdate(propValItem); return; - case Internal.Model.TableRefType.ManyToOne: + case TableRefType.ManyToOne: + case TableRefType.ArrayToMany: return; } @@ -714,7 +716,7 @@ List> LocalGetNavigates(TableInfo tb) { return tb.Properties.Where(a => tb.ColumnsByCs.ContainsKey(a.Key) == false) .Select(a => new NativeTuple(tb.GetTableRef(a.Key, false), a.Value)) - .Where(a => a.Item1 != null && a.Item1.RefType != TableRefType.ManyToOne) + .Where(a => a.Item1 != null && new[] { TableRefType.OneToOne, TableRefType.OneToMany, TableRefType.ManyToMany }.Contains(a.Item1.RefType)) .ToList(); } void LocalEach(DbSet dbset, IEnumerable items, bool isOneToOne) diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index c806edd36..eefbd8530 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -154,6 +154,7 @@ public bool ReadAnonymousField(List _tables, StringBuilder fiel { case TableRefType.ManyToMany: case TableRefType.OneToMany: + case TableRefType.ArrayToMany: continue; } if (_tables.Any(a => a.Alias == $"{map.First().Table.Alias}__{memProp.Name}") == false) continue; @@ -2194,6 +2195,8 @@ void LocalSetSelectProviderAlias(string alias) for (var tidx = 0; tidx < memberTbref.Columns.Count; tidx++) select.Where($"{select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[tidx].Attribute.Name)} = {omtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[tidx].Attribute.Name)}"); break; + case TableRefType.ArrayToMany: + break; } } @@ -2241,6 +2244,8 @@ void LocalInitSelectProvider() if (select != null) return; LocalInitSelectProvider(); continue; + case TableRefType.ArrayToMany: + continue; } } if (exp4.NodeType == ExpressionType.Call) diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index e28b43d1f..36cde5e9d 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -430,6 +430,8 @@ public ISelect IncludeByPropertyName(string property) var curTb = _commonUtils.GetTableByEntity(exp.Type); _commonExpression.ExpressionWhereLambda(_tables, Expression.MakeMemberAccess(exp, curTb.Properties[curTb.ColumnsByCs.First().Value.CsName]), null, null, null); break; + case TableRefType.ArrayToMany: + break; } return this; } @@ -1172,6 +1174,8 @@ public ISelect IncludeMany(Expression a.FullName.EndsWith(trytbTypeLazyName)).FirstOrDefault(); trytb.TypeLazy = type; @@ -989,111 +989,215 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo } } else - { //One To Many - List bindColumns = new List(); - if (pnvBind != null) + { + var isArrayToMany = false; + var lmbdWhere = isLazy ? new StringBuilder() : null; + //Pgsql Array[] To Many + if (common._orm.Ado.DataType == DataType.PostgreSQL) { - foreach (var bi in pnvBind) + //class User { + // public int[] RoleIds { get; set; } + // [Navigate(nameof(RoleIds))] + // public Role[] Roles { get; set; } + //} + //class Role { + // [Navigate(nameof(User.RoleIds))] + // public User[] Users { get; set; } + //} + ColumnInfo trycol = null; + if (tbref.Primarys.Length == 1) { - if (tbref.ColumnsByCs.TryGetValue(bi, out var trybindcol) == false) + if (pnvBind?.Length == 1) + { + if (trytb.ColumnsByCs.TryGetValue(pnvBind[0], out trycol)) + { + if (trycol.CsType.IsArray == true && tbref.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + { + nvref.Exception = new Exception($"导航属性 {trytbTypeName}.{pnv.Name} 特性 [Navigate] 解析错误,{trytbTypeName}.{trycol.CsName} 数组元素 与 {tbrefTypeName}.{tbref.Primarys[0].CsName} 类型不符"); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + } + } + } + if (nvref.Exception == null && trycol == null) + { + var findtbrefPkCsName = tbref.Primarys[0].CsName.TrimStart('_'); + if (findtbrefPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtbrefPkCsName = findtbrefPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); + var findtrytb = pnv.Name; + if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); + findtrytb += tbref.CsName; + if ( + tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtbrefPkCsName}", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtbrefPkCsName}", out trycol) == false //下划线命名 + ) + { + } + if (trycol != null && tbref.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + trycol = null; + } + isArrayToMany = trycol != null; + if (isArrayToMany) { - nvref.Exception = new Exception(CoreStrings.Navigation_ParsingError_NotFound_Property(trytbTypeName, pnv.Name, tbrefTypeName, bi)); + lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName).Append(")"); + nvref.Columns.Add(trycol); + nvref.RefColumns.Add(tbref.Primarys[0]); + nvref.RefEntityType = tbref.Type; + nvref.RefType = TableRefType.ArrayToMany; trytb.AddOrUpdateTableRef(pnv.Name, nvref); - //if (isLazy) throw nvref.Exception; - break; } - bindColumns.Add(trybindcol); - } - } - - PropertyInfo refprop = null; - var refcols = tbref.Properties.Where(z => z.Value.PropertyType == trytb.Type); - refprop = refcols.Count() == 1 ? refcols.First().Value : null; - var lmbdWhere = isLazy ? new StringBuilder() : null; - - if (nvref.Exception == null && bindColumns.Any() && bindColumns.Count != trytb.Primarys.Length) - { - nvref.Exception = new Exception(CoreStrings.Navigation_Bind_Number_Different(trytbTypeName, pnv.Name, bindColumns.Count, trytb.Primarys.Length)); - trytb.AddOrUpdateTableRef(pnv.Name, nvref); - //if (isLazy) throw nvref.Exception; - } - if (trytb.Primarys.Length > 1) - { - if (trytb.Primarys.Select(a => a.CsType.NullableTypeOrThis()).Distinct().Count() == trytb.Primarys.Length) - { - var pkList = trytb.Primarys.ToList(); - bindColumns.Sort((a, b) => pkList.FindIndex(c => c.CsType.NullableTypeOrThis() == a.CsType.NullableTypeOrThis()).CompareTo(pkList.FindIndex(c => c.CsType.NullableTypeOrThis() == b.CsType.NullableTypeOrThis()))); } - else if (string.Compare(string.Join(",", trytb.Primarys.Select(a => a.CsName).OrderBy(a => a)), string.Join(",", bindColumns.Select(a => a.CsName).OrderBy(a => a)), true) == 0) + + if (nvref.Exception == null && trytb.Primarys.Length == 1 && isArrayToMany == false) { - var pkList = trytb.Primarys.ToList(); - bindColumns.Sort((a, b) => pkList.FindIndex(c => string.Compare(c.CsName, a.CsName, true) == 0).CompareTo(pkList.FindIndex(c => string.Compare(c.CsName, b.CsName, true) == 0))); + if (pnvBind?.Length == 1) + { + if (tbref.ColumnsByCs.TryGetValue(pnvBind[0], out trycol)) + { + if (trycol.CsType.IsArray == true && trytb.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + { + nvref.Exception = new Exception($"导航属性 {trytbTypeName}.{pnv.Name} 特性 [Navigate] 解析错误,{trytbTypeName}.{trytb.Primarys[0].CsName} 与 {tbrefTypeName}.{trycol.CsName} 数组元素类型不符"); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + } + } + } + if (nvref.Exception == null && trycol == null) + { + var findtrytbPkCsName = trytb.Primarys[0].CsName.TrimStart('_'); + if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); + var findtrytb = pnv.Name; + if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); + findtrytb += trytb.CsName; + if ( + tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}", out trycol) == false //下划线命名 + ) + { + } + if (trycol != null && trytb.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + trycol = null; + } + isArrayToMany = trycol != null; + if (isArrayToMany) + { + lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName).Append(")"); + nvref.Columns.Add(tbref.Primarys[0]); + nvref.RefColumns.Add(trycol); + nvref.RefEntityType = tbref.Type; + nvref.RefType = TableRefType.ArrayToMany; + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + } } + } - for (var a = 0; nvref.Exception == null && a < trytb.Primarys.Length; a++) + + PropertyInfo refprop = null; + if (isArrayToMany == false) { - var findtrytbPkCsName = trytb.Primarys[a].CsName.TrimStart('_'); - if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); - var findtrytb = pnv.Name; - if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); - findtrytb += trytb.CsName; - - var trycol = bindColumns.Any() ? bindColumns[a] : null; - if (trycol == null && - tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 - tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}", out trycol) == false //下划线命名 - ) + List bindColumns = new List(); + //One To Many + if (pnvBind != null) { - if (refprop != null && - tbref.ColumnsByCs.TryGetValue($"{refprop.Name}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 - tbref.ColumnsByCs.TryGetValue($"{refprop.Name}_{findtrytbPkCsName}", out trycol) == false) //下划线命名 + foreach (var bi in pnvBind) { - + if (tbref.ColumnsByCs.TryGetValue(bi, out var trybindcol) == false) + { + nvref.Exception = new Exception(CoreStrings.Navigation_ParsingError_NotFound_Property(trytbTypeName, pnv.Name, tbrefTypeName, bi)); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + break; + } + bindColumns.Add(trybindcol); } } - if (trycol != null && trycol.CsType.NullableTypeOrThis() != trytb.Primarys[a].CsType.NullableTypeOrThis()) + + var refcols = tbref.Properties.Where(z => z.Value.PropertyType == trytb.Type); + refprop = refcols.Count() == 1 ? refcols.First().Value : null; + + if (nvref.Exception == null && bindColumns.Any() && bindColumns.Count != trytb.Primarys.Length) { - nvref.Exception = new Exception(CoreStrings.OneToMany_ParsingError_InconsistentType(trytbTypeName, pnv.Name, trytb.CsName, trytb.Primarys[a].CsName, tbref.CsName, trycol.CsName)); + nvref.Exception = new Exception(CoreStrings.Navigation_Bind_Number_Different(trytbTypeName, pnv.Name, bindColumns.Count, trytb.Primarys.Length)); trytb.AddOrUpdateTableRef(pnv.Name, nvref); //if (isLazy) throw nvref.Exception; - break; } - if (trycol == null) + if (trytb.Primarys.Length > 1) { - nvref.Exception = new Exception(CoreStrings.OneToMany_NotFound_CorrespondingField(trytbTypeName, pnv.Name, tbref.CsName, findtrytb, findtrytbPkCsName) - + (refprop == null ? "" : CoreStrings.OneToMany_UseNavigate(refprop.Name, findtrytbPkCsName))); - trytb.AddOrUpdateTableRef(pnv.Name, nvref); - //if (isLazy) throw nvref.Exception; - break; + if (trytb.Primarys.Select(a => a.CsType.NullableTypeOrThis()).Distinct().Count() == trytb.Primarys.Length) + { + var pkList = trytb.Primarys.ToList(); + bindColumns.Sort((a, b) => pkList.FindIndex(c => c.CsType.NullableTypeOrThis() == a.CsType.NullableTypeOrThis()).CompareTo(pkList.FindIndex(c => c.CsType.NullableTypeOrThis() == b.CsType.NullableTypeOrThis()))); + } + else if (string.Compare(string.Join(",", trytb.Primarys.Select(a => a.CsName).OrderBy(a => a)), string.Join(",", bindColumns.Select(a => a.CsName).OrderBy(a => a)), true) == 0) + { + var pkList = trytb.Primarys.ToList(); + bindColumns.Sort((a, b) => pkList.FindIndex(c => string.Compare(c.CsName, a.CsName, true) == 0).CompareTo(pkList.FindIndex(c => string.Compare(c.CsName, b.CsName, true) == 0))); + } } + for (var a = 0; nvref.Exception == null && a < trytb.Primarys.Length; a++) + { + var findtrytbPkCsName = trytb.Primarys[a].CsName.TrimStart('_'); + if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); + var findtrytb = pnv.Name; + if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); + findtrytb += trytb.CsName; + + var trycol = bindColumns.Any() ? bindColumns[a] : null; + if (trycol == null && + tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}", out trycol) == false //下划线命名 + ) + { + if (refprop != null && + tbref.ColumnsByCs.TryGetValue($"{refprop.Name}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{refprop.Name}_{findtrytbPkCsName}", out trycol) == false) //下划线命名 + { + + } + } + if (trycol != null && trycol.CsType.NullableTypeOrThis() != trytb.Primarys[a].CsType.NullableTypeOrThis()) + { + nvref.Exception = new Exception(CoreStrings.OneToMany_ParsingError_InconsistentType(trytbTypeName, pnv.Name, trytb.CsName, trytb.Primarys[a].CsName, tbref.CsName, trycol.CsName)); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + break; + } + if (trycol == null) + { + nvref.Exception = new Exception(CoreStrings.OneToMany_NotFound_CorrespondingField(trytbTypeName, pnv.Name, tbref.CsName, findtrytb, findtrytbPkCsName) + + (refprop == null ? "" : CoreStrings.OneToMany_UseNavigate(refprop.Name, findtrytbPkCsName))); + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + //if (isLazy) throw nvref.Exception; + break; + } - nvref.Columns.Add(trytb.Primarys[a]); - nvref.RefColumns.Add(trycol); + nvref.Columns.Add(trytb.Primarys[a]); + nvref.RefColumns.Add(trycol); - if (isLazy && nvref.Exception == null) - { - if (a > 0) lmbdWhere.Append(" && "); - lmbdWhere.Append("a.").Append(trycol.CsName).Append(" == this.").Append(trytb.Primarys[a].CsName); + if (isLazy && nvref.Exception == null) + { + if (a > 0) lmbdWhere.Append(" && "); + lmbdWhere.Append("a.").Append(trycol.CsName).Append(" == this.").Append(trytb.Primarys[a].CsName); - if (refprop == null) - { //加载成功后,把列表对应的导航属性值设置为 this,比如 Select().ToOne().Topics 下的 TopicType 属性值全部为 this - var findtrytbName = trycol.CsName; - if (findtrytbName.EndsWith(trytb.Primarys.First().CsName)) - { - findtrytbName = findtrytbName.Remove(findtrytbName.Length - trytb.Primarys.First().CsName.Length).TrimEnd('_'); - if (tbref.Properties.TryGetValue(findtrytbName, out refprop) && refprop.PropertyType != trytb.Type) - refprop = null; + if (refprop == null) + { //加载成功后,把列表对应的导航属性值设置为 this,比如 Select().ToOne().Topics 下的 TopicType 属性值全部为 this + var findtrytbName = trycol.CsName; + if (findtrytbName.EndsWith(trytb.Primarys.First().CsName)) + { + findtrytbName = findtrytbName.Remove(findtrytbName.Length - trytb.Primarys.First().CsName.Length).TrimEnd('_'); + if (tbref.Properties.TryGetValue(findtrytbName, out refprop) && refprop.PropertyType != trytb.Type) + refprop = null; + } } } } - } - if (nvref.Columns.Count > 0 && nvref.RefColumns.Count > 0) - { - nvref.RefEntityType = tbref.Type; - nvref.RefType = TableRefType.OneToMany; - trytb.AddOrUpdateTableRef(pnv.Name, nvref); - } + if (nvref.Columns.Count > 0 && nvref.RefColumns.Count > 0) + { + nvref.RefEntityType = tbref.Type; + nvref.RefType = TableRefType.OneToMany; + trytb.AddOrUpdateTableRef(pnv.Name, nvref); + } + } if (isLazy) { cscode.Append(" private bool __lazy__").Append(pnv.Name).AppendLine(" = false;") @@ -1133,7 +1237,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo } } else - { //一对一、多对一 + { + //一对一、多对一 var tbref = pnv.PropertyType == trytb.Type ? trytb : GetTableByEntity(pnv.PropertyType, common); //可能是父子关系 if (tbref == null) return; if (tbref.Primarys.Any() == false) From 5e5d4642245a77cacae50776bd504e149bb23afe Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 4 Jun 2022 12:26:00 +0800 Subject: [PATCH 12/20] add ArrayToMany IncludeMany --- .../SelectProvider/Select1Provider.cs | 123 +++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 36cde5e9d..04e2b7119 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -670,10 +670,11 @@ public ISelect IncludeMany(Expression); var listValueExp = Expression.Parameter(typeof(List), "listValue"); var setListValue = membersExpNotNull == null ? Expression.Lambda>>( - collMem.Type == typeof(ObservableCollection) ? + isObservableCollection ? (Expression)Expression.IfThen( Expression.NotEqual(listValueExp, Expression.Constant(null, typeof(List))), Expression.Assign(Expression.MakeMemberAccess(membersExp, collMem.Member), Expression.New(typeof(ObservableCollection).GetConstructor(new[] { typeof(List) }), listValueExp)) @@ -681,7 +682,7 @@ public ISelect IncludeMany(Expression>>(Expression.IfThen(membersExpNotNull, - collMem.Type == typeof(ObservableCollection) ? + isObservableCollection ? (Expression)Expression.IfThen( Expression.NotEqual(listValueExp, Expression.Constant(null, typeof(List))), Expression.Assign(Expression.MakeMemberAccess(membersExp, collMem.Member), Expression.New(typeof(ObservableCollection).GetConstructor(new[] { typeof(List) }), listValueExp)) @@ -1175,6 +1176,124 @@ public ISelect IncludeMany(Expression(); + var tbref2 = _commonUtils.GetTableByEntity(tbref.RefEntityType); + if (tbref.RefColumns[0] == tbref2.Primarys[0]) + { + var listKeys = list.Select(a => + { + var arrVal = getListValue(a, tbref.Columns[0].CsName, 0) as Array; + var arrObjVal = new object[arrVal.Length]; + arrVal.CopyTo(arrObjVal, 0); + return arrObjVal; + }).Where(a => a != null).ToArray(); + var arrExp = Expression.NewArrayInit(tbref.RefColumns[0].CsType, listKeys.SelectMany(a => a).Distinct() + .Select(a => Expression.Constant(Utils.GetDataReaderValue(tbref.RefColumns[0].CsType, a), tbref.RefColumns[0].CsType)).ToArray()); + var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); + var containsMethod = _dicTypeMethod.GetOrAdd(tbref.RefColumns[0].CsType, et => new ConcurrentDictionary()).GetOrAdd("Contains", mn => + typeof(Enumerable).GetMethods().Where(a => a.Name == mn).First()).MakeGenericMethod(tbref.RefColumns[0].CsType); + var refCol = Expression.MakeMemberAccess(otmExpParm1, tbref2.Properties[tbref.RefColumns[0].CsName]); + subSelect.Where(Expression.Lambda>( + Expression.Call(null, containsMethod, arrExp, refCol), otmExpParm1)); + + if (isAsync) + { +#if net40 +#else + if (selectExp == null) subList = await subSelect.ToListAsync(true, cancellationToken); + else subList = await subSelect.ToListAsync(selectExp, cancellationToken); +#endif + } + else + { + if (selectExp == null) subList = subSelect.ToList(true); + else subList = subSelect.ToList(selectExp); + } + + if (subList.Any() == false) + { + foreach (var item in list) + setListValue(item, new List()); + return; + } + var dicSubList = subList.ToDictionary(a => EntityUtilExtensions.GetEntityValueWithPropertyName(_orm, tbref.RefEntityType, a, tbref.RefColumns[0].CsName)?.ToString(), a => a); + + var parentNavs = new List(); + foreach (var navProp in tbref2.Properties) + { + if (tbref2.ColumnsByCs.ContainsKey(navProp.Key)) continue; + if (tbref2.ColumnsByCsIgnore.ContainsKey(navProp.Key)) continue; + var tr2ref = tbref2.GetTableRef(navProp.Key, false); + if (tr2ref == null) continue; + if (tr2ref.RefType != TableRefType.ManyToOne) continue; + if (tr2ref.RefEntityType != tb.Type) continue; + if (string.Join(",", tr2ref.Columns.Select(a => a.CsName).OrderBy(a => a)) != string.Join(",", tbref.RefColumns.Select(a => a.CsName).OrderBy(a => a))) continue; //- 修复 IncludeMany 只填充子属性中双向关系的 ManyToOne 对象值;防止把 ManyToOne 多个相同类型的导航属性值都填充了 + parentNavs.Add(navProp.Key); + } + for (var y = 0; y < list.Count; y++) + { + var item = list[y]; + var dicListKeys = listKeys[y]; + var navs = new List(); + foreach (var dlk in dicListKeys) + { + if (dlk == null) + { + navs.Add(null); + continue; + } + var dicListKey = dlk.ToString(); + dicSubList.TryGetValue(dicListKey, out var nav); + navs.Add(nav); + } + setListValue(item, navs); + } + dicSubList.Clear(); + subList.Clear(); + } + else if (tbref.Columns[0] == tb.Primarys[0]) + { + var listKeys = list.Select(a => getListValue(a, tbref.Columns[0].CsName, 0)).Distinct() + .Select(a => Utils.GetDataReaderValue(tbref.RefColumns[0].CsType.GetElementType(), a)).ToArray(); + var listKeysSql = _commonUtils.GetNoneParamaterSqlValue(subSelect._params, "arrtm", tbref.RefColumns[0], tbref.RefColumns[0].CsType, listKeys); + subSelect.Where($"{subSelectT1Alias}.{_commonUtils.QuoteSqlName(tbref.RefColumns[0].Attribute.Name)} && {listKeysSql}"); + + if (isAsync) + { +#if net40 +#else + if (selectExp == null) subList = await subSelect.ToListAsync(true, cancellationToken); + else subList = await subSelect.ToListAsync(selectExp, cancellationToken); +#endif + } + else + { + if (selectExp == null) subList = subSelect.ToList(true); + else subList = subSelect.ToList(selectExp); + } + + if (subList.Any() == false) + { + foreach (var item in list) + setListValue(item, new List()); + return; + } + var subListDic = subList.Select(a => { + var arrVal = EntityUtilExtensions.GetEntityValueWithPropertyName(_orm, tbref2.Type, a, tbref.RefColumns[0].CsName) as Array; + var arrObjVal = new object[arrVal.Length]; + arrVal.CopyTo(arrObjVal, 0); + return arrObjVal.Select(b => NativeTuple.Create(a, b?.ToString())); + }).SelectMany(a => a).GroupBy(a => a.Item2).ToDictionary(a => a.Key, a => a.Select(b => b.Item1).ToList()); + foreach (var item in list) + { + var itemKey = getListValue(item, tbref.Columns[0].CsName, 0)?.ToString(); + subListDic.TryGetValue(itemKey, out var navs); + setListValue(item, navs); + } + } + } break; } }; From 341155267324e6afe1b84d3edf780709e0ed7b56 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 4 Jun 2022 15:23:24 +0800 Subject: [PATCH 13/20] rename ArrayToMany to PgArrayToMany --- FreeSql.DbContext/DbSet/DbSetAsync.cs | 4 ++-- FreeSql.DbContext/DbSet/DbSetSync.cs | 4 ++-- FreeSql/Internal/CommonExpression.cs | 14 +++++++++--- .../SelectProvider/Select1Provider.cs | 7 +++--- FreeSql/Internal/Model/TableInfo.cs | 2 +- FreeSql/Internal/UtilsExpressionTree.cs | 22 ++++++++++++++----- 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/FreeSql.DbContext/DbSet/DbSetAsync.cs b/FreeSql.DbContext/DbSet/DbSetAsync.cs index 5bdccd0cf..ea5d35d18 100644 --- a/FreeSql.DbContext/DbSet/DbSetAsync.cs +++ b/FreeSql.DbContext/DbSet/DbSetAsync.cs @@ -157,7 +157,7 @@ async public Task SaveManyAsync(TEntity item, string propertyName, CancellationT { case TableRefType.OneToOne: case TableRefType.ManyToOne: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: throw new ArgumentException(DbContextStrings.PropertyOfType_IsNot_OneToManyOrManyToMany(_table.Type.FullName, propertyName)); } @@ -227,7 +227,7 @@ async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyNam else await refSet.AddOrUpdateAsync(propValItem); return; case TableRefType.ManyToOne: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: return; } diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index fc9bb1e10..949753555 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -168,7 +168,7 @@ public void SaveMany(TEntity item, string propertyName) { case TableRefType.OneToOne: case TableRefType.ManyToOne: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: throw new ArgumentException(DbContextStrings.PropertyOfType_IsNot_OneToManyOrManyToMany(_table.Type.FullName, propertyName)); } @@ -238,7 +238,7 @@ void AddOrUpdateNavigate(TEntity item, bool isAdd, string propertyName) else refSet.AddOrUpdate(propValItem); return; case TableRefType.ManyToOne: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: return; } diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index eefbd8530..428efb503 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -154,7 +154,7 @@ public bool ReadAnonymousField(List _tables, StringBuilder fiel { case TableRefType.ManyToMany: case TableRefType.OneToMany: - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: continue; } if (_tables.Any(a => a.Alias == $"{map.First().Table.Alias}__{memProp.Name}") == false) continue; @@ -2195,7 +2195,13 @@ void LocalSetSelectProviderAlias(string alias) for (var tidx = 0; tidx < memberTbref.Columns.Count; tidx++) select.Where($"{select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[tidx].Attribute.Name)} = {omtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[tidx].Attribute.Name)}"); break; - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: + var amtReftbname = e.FreeParse(Expression.MakeMemberAccess(memberExp.Expression, exp3Tb.Properties[exp3Tb.ColumnsByPosition[0].CsName])); + amtReftbname = amtReftbname.Substring(0, amtReftbname.Length - commonExp._common.QuoteSqlName(exp3Tb.ColumnsByPosition[0].Attribute.Name).Length - 1); + if (memberTbref.RefColumns[0] == select._tables[0].Table.Primarys[0]) + select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)} @> {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)}"); + else if (memberTbref.Columns[0] == select._tables[0].Table.Primarys[0]) + select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)} @> {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)}"); break; } @@ -2244,7 +2250,9 @@ void LocalInitSelectProvider() if (select != null) return; LocalInitSelectProvider(); continue; - case TableRefType.ArrayToMany: + case TableRefType.PgArrayToMany: + if (select != null) return; + LocalInitSelectProvider(); continue; } } diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 04e2b7119..838e01ebf 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -418,6 +418,7 @@ public ISelect IncludeByPropertyName(string property) { case TableRefType.ManyToMany: case TableRefType.OneToMany: + case TableRefType.PgArrayToMany: var funcType = typeof(Func<,>).MakeGenericType(_tables[0].Table.Type, typeof(IEnumerable<>).MakeGenericType(parTbref.RefEntityType)); var navigateSelector = Expression.Lambda(funcType, exp, _tables[0].Parameter); var incMethod = this.GetType().GetMethod("IncludeMany"); @@ -430,8 +431,6 @@ public ISelect IncludeByPropertyName(string property) var curTb = _commonUtils.GetTableByEntity(exp.Type); _commonExpression.ExpressionWhereLambda(_tables, Expression.MakeMemberAccess(exp, curTb.Properties[curTb.ColumnsByCs.First().Value.CsName]), null, null, null); break; - case TableRefType.ArrayToMany: - break; } return this; } @@ -1175,7 +1174,7 @@ public ISelect IncludeMany(Expression(); @@ -1256,7 +1255,7 @@ public ISelect IncludeMany(Expression getListValue(a, tbref.Columns[0].CsName, 0)).Distinct() - .Select(a => Utils.GetDataReaderValue(tbref.RefColumns[0].CsType.GetElementType(), a)).ToArray(); + .Select(a => Utils.GetDataReaderValue(tbref.RefColumns[0].CsType.GetElementType(), a)).ToArray(); var listKeysSql = _commonUtils.GetNoneParamaterSqlValue(subSelect._params, "arrtm", tbref.RefColumns[0], tbref.RefColumns[0].CsType, listKeys); subSelect.Where($"{subSelectT1Alias}.{_commonUtils.QuoteSqlName(tbref.RefColumns[0].Attribute.Name)} && {listKeysSql}"); diff --git a/FreeSql/Internal/Model/TableInfo.cs b/FreeSql/Internal/Model/TableInfo.cs index 2496d63c2..1349e952c 100644 --- a/FreeSql/Internal/Model/TableInfo.cs +++ b/FreeSql/Internal/Model/TableInfo.cs @@ -150,6 +150,6 @@ public class TableRef } public enum TableRefType { - OneToOne, ManyToOne, OneToMany, ManyToMany, ArrayToMany + OneToOne, ManyToOne, OneToMany, ManyToMany, PgArrayToMany } } \ No newline at end of file diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 271edc49c..73163bcc3 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Data; using System.Data.Common; using System.Linq; @@ -623,6 +624,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo //List 或 ICollection,一对多、多对多 var propElementType = pnv.PropertyType.GetGenericArguments().FirstOrDefault() ?? pnv.PropertyType.GetElementType(); + var propTypeIsObservableCollection = propElementType != null && pnv.PropertyType == typeof(ObservableCollection<>).MakeGenericType(propElementType); if (propElementType != null) { if (typeof(IEnumerable).IsAssignableFrom(pnv.PropertyType) == false) return; @@ -992,17 +994,18 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo { var isArrayToMany = false; var lmbdWhere = isLazy ? new StringBuilder() : null; + var cscodeExtLogic = ""; //Pgsql Array[] To Many if (common._orm.Ado.DataType == DataType.PostgreSQL) { //class User { // public int[] RoleIds { get; set; } // [Navigate(nameof(RoleIds))] - // public Role[] Roles { get; set; } + // public List Roles { get; set; } //} //class Role { // [Navigate(nameof(User.RoleIds))] - // public User[] Users { get; set; } + // public List Users { get; set; } //} ColumnInfo trycol = null; if (tbref.Primarys.Length == 1) @@ -1038,11 +1041,14 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName).Append(")"); + cscodeExtLogic = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; + lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName); + if (trycol.CsType.GetElementType().IsNullableType() == false && tbref.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); + lmbdWhere.Append(")"); nvref.Columns.Add(trycol); nvref.RefColumns.Add(tbref.Primarys[0]); nvref.RefEntityType = tbref.Type; - nvref.RefType = TableRefType.ArrayToMany; + nvref.RefType = TableRefType.PgArrayToMany; trytb.AddOrUpdateTableRef(pnv.Name, nvref); } } @@ -1080,11 +1086,14 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName).Append(")"); + cscodeExtLogic = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName); + if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); + lmbdWhere.Append(")"); nvref.Columns.Add(tbref.Primarys[0]); nvref.RefColumns.Add(trycol); nvref.RefEntityType = tbref.Type; - nvref.RefType = TableRefType.ArrayToMany; + nvref.RefType = TableRefType.PgArrayToMany; trytb.AddOrUpdateTableRef(pnv.Name, nvref); } } @@ -1205,6 +1214,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo if (vp?.Item2 == true) { //get 重写 cscode.Append(" ").Append(propGetModification).Append(" get {\r\n") + .Append(cscodeExtLogic) .Append(" if (base.").Append(pnv.Name).Append(" == null && __lazy__").Append(pnv.Name).AppendLine(" == false) {"); if (nvref.Exception == null) From 50a8d979a581472f09571555a45cbe9c87104181 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 4 Jun 2022 16:10:20 +0800 Subject: [PATCH 14/20] fix PgArrayToMany --- FreeSql.Tests/FreeSql.Tests/Issues/623.cs | 2 +- FreeSql/Extensions/FreeSqlGlobalExtensions.cs | 2 +- FreeSql/FreeSql.xml | 17 +++++++++++++++++ FreeSql/Internal/Model/TableInfo.cs | 18 +++++++++++++++++- FreeSql/Internal/UtilsExpressionTree.cs | 17 +++++++++++------ 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/Issues/623.cs b/FreeSql.Tests/FreeSql.Tests/Issues/623.cs index db0da73ba..5898fc433 100644 --- a/FreeSql.Tests/FreeSql.Tests/Issues/623.cs +++ b/FreeSql.Tests/FreeSql.Tests/Issues/623.cs @@ -44,7 +44,7 @@ public void MySqlGroupBy() FacilityCount = a.Sum(a.Value.FacilityCount), FacilityOpenCount = a.Sum(a.Value.FacilityOpenCount) }); - Assert.Equal(@"SELECT a.`Dot`, sum(a.`FacilityCount`) as1, sum(a.`FacilityOpenCount`) as2 + Assert.Equal(@"SELECT a.`Dot` as1, sum(a.`FacilityCount`) as2, sum(a.`FacilityOpenCount`) as3 FROM `ts_facility` a WHERE (a.`Date` = cast(date_format('2020-12-30 00:00:00.000','%Y-%m-%d') as datetime)) AND (((a.`EnterpriseId`) in (5))) AND (a.`FacilityType` = 1) GROUP BY a.`Dot`", sql); diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs index e0ffb9c1a..9124dd68f 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs @@ -48,7 +48,7 @@ public static partial class FreeSqlGlobalExtensions }); public static bool IsIntegerType(this Type that) => that == null ? false : (_dicIsNumberType.Value.TryGetValue(that, out var tryval) ? tryval : false); public static bool IsNumberType(this Type that) => that == null ? false : _dicIsNumberType.Value.ContainsKey(that); - public static bool IsNullableType(this Type that) => that.IsArray == false && that?.FullName.StartsWith("System.Nullable`1[") == true; + public static bool IsNullableType(this Type that) => that == null ? false : (that.IsArray == false && that.FullName.StartsWith("System.Nullable`1[") == true); public static bool IsAnonymousType(this Type that) => that == null ? false : (that.FullName.StartsWith("<>f__AnonymousType") || that.FullName.StartsWith("VB$AnonymousType")); public static bool IsArrayOrList(this Type that) => that == null ? false : (that.IsArray || typeof(IList).IsAssignableFrom(that)); public static Type NullableTypeOrThis(this Type that) => that?.IsNullableType() == true ? that.GetGenericArguments().First() : that; diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 3866a3677..160a7397d 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -4269,6 +4269,23 @@ 中间表,多对多 + + + PostgreSQL 数组类型专属功能 + 方式一:select * from Role where Id in (RoleIds) + class User { + ____public int[] RoleIds { get; set; } + ____[Navigate(nameof(RoleIds))] + ____public List<Role> Roles { get; set; } + } + 方式二:select * from User where RoleIds @> Id + class Role { + ____public int Id { get; set; } + ____[Navigate(nameof(User.RoleIds))] + ____public List<User> Users { get; set; } + } + + 是否可用 diff --git a/FreeSql/Internal/Model/TableInfo.cs b/FreeSql/Internal/Model/TableInfo.cs index 1349e952c..6c8583149 100644 --- a/FreeSql/Internal/Model/TableInfo.cs +++ b/FreeSql/Internal/Model/TableInfo.cs @@ -150,6 +150,22 @@ public class TableRef } public enum TableRefType { - OneToOne, ManyToOne, OneToMany, ManyToMany, PgArrayToMany + OneToOne, ManyToOne, OneToMany, ManyToMany, + /// + /// PostgreSQL 数组类型专属功能 + /// 方式一:select * from Role where Id in (RoleIds) + /// class User { + /// ____public int[] RoleIds { get; set; } + /// ____[Navigate(nameof(RoleIds))] + /// ____public List<Role> Roles { get; set; } + /// } + /// 方式二:select * from User where RoleIds @> Id + /// class Role { + /// ____public int Id { get; set; } + /// ____[Navigate(nameof(User.RoleIds))] + /// ____public List<User> Users { get; set; } + /// } + /// + PgArrayToMany } } \ No newline at end of file diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 73163bcc3..7e852d3be 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -1014,7 +1014,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo { if (trytb.ColumnsByCs.TryGetValue(pnvBind[0], out trycol)) { - if (trycol.CsType.IsArray == true && tbref.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + if (trycol.CsType.IsArray == false) trycol = null; + else if (trycol != null && tbref.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) { nvref.Exception = new Exception($"导航属性 {trytbTypeName}.{pnv.Name} 特性 [Navigate] 解析错误,{trytbTypeName}.{trycol.CsName} 数组元素 与 {tbrefTypeName}.{tbref.Primarys[0].CsName} 类型不符"); trytb.AddOrUpdateTableRef(pnv.Name, nvref); @@ -1022,7 +1023,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo } } } - if (nvref.Exception == null && trycol == null) + if (pnvBind == null && trycol == null) { var findtbrefPkCsName = tbref.Primarys[0].CsName.TrimStart('_'); if (findtbrefPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtbrefPkCsName = findtbrefPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); @@ -1059,7 +1060,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo { if (tbref.ColumnsByCs.TryGetValue(pnvBind[0], out trycol)) { - if (trycol.CsType.IsArray == true && trytb.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) + if (trycol.CsType.IsArray == false) trycol = null; + else if (trytb.Primarys[0].CsType.NullableTypeOrThis() != trycol.CsType.GetElementType().NullableTypeOrThis()) { nvref.Exception = new Exception($"导航属性 {trytbTypeName}.{pnv.Name} 特性 [Navigate] 解析错误,{trytbTypeName}.{trytb.Primarys[0].CsName} 与 {tbrefTypeName}.{trycol.CsName} 数组元素类型不符"); trytb.AddOrUpdateTableRef(pnv.Name, nvref); @@ -1067,7 +1069,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo } } } - if (nvref.Exception == null && trycol == null) + if (pnvBind != null && trycol == null) { var findtrytbPkCsName = trytb.Primarys[0].CsName.TrimStart('_'); if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); @@ -1086,9 +1088,12 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - cscodeExtLogic = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName); - if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); + if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) + { + lmbdWhere.Append(".Value"); + cscodeExtLogic = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + } lmbdWhere.Append(")"); nvref.Columns.Add(tbref.Primarys[0]); nvref.RefColumns.Add(trycol); From a50c14e77a9fb5618497fdb9e0ba2fa8917fda55 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 13:36:18 +0800 Subject: [PATCH 15/20] =?UTF-8?q?-=20=E5=A4=84=E7=90=86=20PgArrayToMany=20?= =?UTF-8?q?AsSelect=20=E5=AD=90=E6=9F=A5=E8=AF=A2=20#1145?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.DbContext/DbSet/DbSetSync.cs | 6 + FreeSql/Internal/CommonExpression.cs | 276 ++++++++++++++------------- 2 files changed, 151 insertions(+), 131 deletions(-) diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index 949753555..cc6f8a3e4 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -862,6 +862,12 @@ void LocalEach(DbSet dbset, IEnumerable items, bool isOneToOne) } } + var atms = navs.Where(a => a.Item1.RefType == TableRefType.PgArrayToMany).ToList(); + if (atms.Any()) + { + + } + if (dbset == rootDbSet) { if (CanRemove(data, true) == false) return; diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 428efb503..b7918b54e 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -1195,162 +1195,176 @@ public string ExpressionLambdaToSql(Expression exp, ExpTSC tsc) if (fsql != null) { if (asSelectParentExp != null) - { //执行 AsSelect() 的关联,OneToMany,ManyToMany + { + //执行 AsSelect() 的关联,OneToMany,ManyToMany,PgArrayToMany if (fsqltables[0].Parameter == null) { fsqltables[0].Alias = $"tb_{fsqltables.Count}"; fsqltables[0].Parameter = Expression.Parameter(asSelectEntityType, fsqltables[0].Alias); } - var fsqlWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(asSelectEntityType, asSelectEntityType3 => - typeof(ISelect<>).MakeGenericType(asSelectEntityType3).GetMethod("Where", new[] { - typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(asSelectEntityType3, typeof(bool))) - })); + var parm123Tb = _common.GetTableByEntity(asSelectParentExp.Type); var parm123Ref = parm123Tb.GetTableRef(asSelectParentExp1.Member.Name, true); if (parm123Ref != null) { - var fsqlWhereParam = fsqltables.First().Parameter; //Expression.Parameter(asSelectEntityType); - Expression fsqlWhereExp = null; - if (parm123Ref.RefType == TableRefType.ManyToMany) + if (parm123Ref.RefType == TableRefType.PgArrayToMany) { - //g.mysql.Select().Where(a => g.mysql.Select().Where(b => b.Tag_id == a.Id && b.Song_id == 1).Any()); - var manyTb = _common.GetTableByEntity(parm123Ref.RefMiddleEntityType); - var manySubSelectWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => - typeof(ISelect<>).MakeGenericType(refMiddleEntityType3).GetMethod("Where", new[] { - typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(refMiddleEntityType3, typeof(bool))) - })); - var manySubSelectWhereSql = _dicExpressionLambdaToSqlAsSelectWhereSqlMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => - typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(refMiddleEntityType3), refMiddleEntityType3).GetMethod("Where", new[] { typeof(string), typeof(object) })); - var manySubSelectAsSelectExp = _dicFreeSqlGlobalExtensionsAsSelectExpression.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => - Expression.Call( - typeof(FreeSqlGlobalExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(mfil => mfil.Name == "AsSelect" && mfil.GetParameters().Length == 1).FirstOrDefault()?.MakeGenericMethod(refMiddleEntityType3), - Expression.Constant(Activator.CreateInstance(typeof(List<>).MakeGenericType(refMiddleEntityType3))) - )); - var manyMainParam = tsc._tables[0].Parameter; - var manySubSelectWhereParam = Expression.Parameter(parm123Ref.RefMiddleEntityType, $"M{fsqlWhereParam.Name}_M{asSelectParentExp.ToString().Replace(".", "__")}");//, $"{fsqlWhereParam.Name}__"); - Expression manySubSelectWhereExp = null; - for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) + var amtReftbname = ExpressionLambdaToSql(Expression.MakeMemberAccess(asSelectParentExp, parm123Tb.Properties[parm123Tb.ColumnsByPosition[0].CsName]), tsc); + amtReftbname = amtReftbname.Substring(0, amtReftbname.Length - _common.QuoteSqlName(parm123Tb.ColumnsByPosition[0].Attribute.Name).Length - 1); + if (parm123Ref.RefColumns[0] == fsqltables[0].Table.Primarys[0]) + (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)} @> {fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)}").Append(")"); + else if (parm123Ref.Columns[0] == fsqltables[0].Table.Primarys[0]) + (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)} @> {fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)}").Append(")"); + } + else + { + var fsqlWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(asSelectEntityType, asSelectEntityType3 => + typeof(ISelect<>).MakeGenericType(asSelectEntityType3).GetMethod("Where", new[] { + typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(asSelectEntityType3, typeof(bool))) + })); + var fsqlWhereParam = fsqltables.First().Parameter; //Expression.Parameter(asSelectEntityType); + Expression fsqlWhereExp = null; + if (parm123Ref.RefType == TableRefType.ManyToMany) { - var col1 = parm123Ref.MiddleColumns[mn]; - var col2 = parm123Ref.Columns[mn]; - var pexp1 = Expression.Property(manySubSelectWhereParam, col1.CsName); - var pexp2 = Expression.Property(asSelectParentExp, col2.CsName); - if (col1.CsType != col2.CsType) + //g.mysql.Select().Where(a => g.mysql.Select().Where(b => b.Tag_id == a.Id && b.Song_id == 1).Any()); + var manyTb = _common.GetTableByEntity(parm123Ref.RefMiddleEntityType); + var manySubSelectWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => + typeof(ISelect<>).MakeGenericType(refMiddleEntityType3).GetMethod("Where", new[] { + typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(refMiddleEntityType3, typeof(bool))) + })); + var manySubSelectWhereSql = _dicExpressionLambdaToSqlAsSelectWhereSqlMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => + typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(refMiddleEntityType3), refMiddleEntityType3).GetMethod("Where", new[] { typeof(string), typeof(object) })); + var manySubSelectAsSelectExp = _dicFreeSqlGlobalExtensionsAsSelectExpression.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 => + Expression.Call( + typeof(FreeSqlGlobalExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(mfil => mfil.Name == "AsSelect" && mfil.GetParameters().Length == 1).FirstOrDefault()?.MakeGenericMethod(refMiddleEntityType3), + Expression.Constant(Activator.CreateInstance(typeof(List<>).MakeGenericType(refMiddleEntityType3))) + )); + var manyMainParam = tsc._tables[0].Parameter; + var manySubSelectWhereParam = Expression.Parameter(parm123Ref.RefMiddleEntityType, $"M{fsqlWhereParam.Name}_M{asSelectParentExp.ToString().Replace(".", "__")}");//, $"{fsqlWhereParam.Name}__"); + Expression manySubSelectWhereExp = null; + for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) { - if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); - if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); + var col1 = parm123Ref.MiddleColumns[mn]; + var col2 = parm123Ref.Columns[mn]; + var pexp1 = Expression.Property(manySubSelectWhereParam, col1.CsName); + var pexp2 = Expression.Property(asSelectParentExp, col2.CsName); + if (col1.CsType != col2.CsType) + { + if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); + if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); + } + var tmpExp = Expression.Equal(pexp1, pexp2); + if (mn == 0) manySubSelectWhereExp = tmpExp; + else manySubSelectWhereExp = Expression.AndAlso(manySubSelectWhereExp, tmpExp); } - var tmpExp = Expression.Equal(pexp1, pexp2); - if (mn == 0) manySubSelectWhereExp = tmpExp; - else manySubSelectWhereExp = Expression.AndAlso(manySubSelectWhereExp, tmpExp); + var manySubSelectExpBoy = Expression.Call( + manySubSelectAsSelectExp, + manySubSelectWhere, + Expression.Lambda( + manySubSelectWhereExp, + manySubSelectWhereParam + ) + ); + Expression fsqlManyWhereExp = null; + for (var mn = 0; mn < parm123Ref.RefColumns.Count; mn++) + { + var col1 = parm123Ref.RefColumns[mn]; + var col2 = parm123Ref.MiddleColumns[mn + parm123Ref.Columns.Count + mn]; + var pexp1 = Expression.Property(fsqlWhereParam, col1.CsName); + var pexp2 = Expression.Property(manySubSelectWhereParam, col2.CsName); + if (col1.CsType != col2.CsType) + { + if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); + if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); + } + var tmpExp = Expression.Equal(pexp1, pexp2); + if (mn == 0) fsqlManyWhereExp = tmpExp; + else fsqlManyWhereExp = Expression.AndAlso(fsqlManyWhereExp, tmpExp); + } + MethodInfo manySubSelectAggMethod = null; + switch (exp3.Method.Name) //https://github.com/dotnetcore/FreeSql/issues/362 + { + case "Any": + case "Count": + fsqltables.Add(new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.Parent }); + fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); + var sql2 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { "1" })?.ToString(); + if (string.IsNullOrEmpty(sql2) == false) + manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectWhereSql, Expression.Constant($"exists({sql2.Replace(" \r\n", " \r\n ")})"), Expression.Constant(null)); + manySubSelectAggMethod = _dicExpressionLambdaToSqlAsSelectAggMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, _ => new ConcurrentDictionary()).GetOrAdd(exp3.Method.Name, exp3MethodName => + typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(parm123Ref.RefMiddleEntityType), parm123Ref.RefMiddleEntityType).GetMethod(exp3MethodName, new Type[0])); + manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectAggMethod); + break; + case "Sum": + case "Min": + case "Max": + case "Avg": + case "ToList": + case "ToOne": + case "First": + //解析:string.Join(",", w.Roles.AsSelect().ToList(b => b.RoleName) + var exp3Args0 = (exp3.Arguments[0] as UnaryExpression)?.Operand as LambdaExpression; + manySubSelectAggMethod = _dicSelectMethodToSql.GetOrAdd(fsqlType, fsqlType2 => + fsqlType2.GetMethods().Where(a => a.Name == "ToSql" && a.GetParameters().Length == 2 && a.GetParameters()[1].ParameterType == typeof(FieldAliasOptions) && a.GetGenericArguments().Length == 1).FirstOrDefault()); + if (manySubSelectAggMethod == null || exp3Args0 == null) throw new ArgumentException(CoreStrings.ManyToMany_AsSelect_NotSupport_Sum_Avg_etc); + manySubSelectAggMethod = manySubSelectAggMethod.MakeGenericMethod(exp3Args0.ReturnType); + var fsqls0p = fsql as Select0Provider; + var fsqls0pWhere = fsqls0p._where.ToString(); + fsqls0p._where.Clear(); + var fsqltablesLast = new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.InnerJoin }; + fsqltables.Add(fsqltablesLast); + fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); + fsqltablesLast.NavigateCondition = fsqls0p._where.ToString(); + if (fsqltablesLast.NavigateCondition.StartsWith(" AND (")) fsqltablesLast.NavigateCondition = fsqltablesLast.NavigateCondition.Substring(6, fsqltablesLast.NavigateCondition.Length - 7); + fsqls0p._where.Clear().Append(fsqls0pWhere); + var tsc3 = tsc.CloneDisableDiyParse(); + tsc3._tables = tsc._tables.ToList(); + var where2 = ExpressionLambdaToSql(Expression.Lambda(manySubSelectWhereExp, manySubSelectWhereParam), tsc3); + if (string.IsNullOrEmpty(where2) == false) fsqls0p._where.Append(" AND (").Append(where2).Append(")"); + + switch (exp3.Method.Name) + { + case "Sum": + case "Min": + case "Max": + case "Avg": + var map = new ReadAnonymousTypeInfo(); + var field = new StringBuilder(); + var index = -1; + + for (var a = 0; a < exp3Args0.Parameters.Count; a++) fsqls0p._tables[a].Parameter = exp3Args0.Parameters[a]; + ReadAnonymousField(fsqls0p._tables, field, map, ref index, exp3Args0, null, null, null, null, null, false); + var fieldSql = field.Length > 0 ? field.Remove(0, 2).ToString() : null; + + var sql4 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { $"{exp3.Method.Name.ToLower()}({fieldSql})" })?.ToString(); + asSelectBefores.Clear(); + return _common.IsNull($"({sql4.Replace(" \r\n", " \r\n ")})", formatSql(exp3.Method.ReturnType.CreateInstanceGetDefaultValue(), exp3.Method.ReturnType, null, null)); + } + + var sql3 = manySubSelectAggMethod.Invoke(fsql, new object[] { exp3Args0, FieldAliasOptions.AsProperty }) as string; + asSelectBefores.Clear(); + return $"({sql3.Replace(" \r\n", " \r\n ")})"; + } + asSelectBefores.Clear(); + return ExpressionLambdaToSql(manySubSelectExpBoy, tsc); } - var manySubSelectExpBoy = Expression.Call( - manySubSelectAsSelectExp, - manySubSelectWhere, - Expression.Lambda( - manySubSelectWhereExp, - manySubSelectWhereParam - ) - ); - Expression fsqlManyWhereExp = null; - for (var mn = 0; mn < parm123Ref.RefColumns.Count; mn++) + for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) { var col1 = parm123Ref.RefColumns[mn]; - var col2 = parm123Ref.MiddleColumns[mn + parm123Ref.Columns.Count + mn]; + var col2 = parm123Ref.Columns[mn]; var pexp1 = Expression.Property(fsqlWhereParam, col1.CsName); - var pexp2 = Expression.Property(manySubSelectWhereParam, col2.CsName); + var pexp2 = Expression.Property(asSelectParentExp, col2.CsName); if (col1.CsType != col2.CsType) { if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); } var tmpExp = Expression.Equal(pexp1, pexp2); - if (mn == 0) fsqlManyWhereExp = tmpExp; - else fsqlManyWhereExp = Expression.AndAlso(fsqlManyWhereExp, tmpExp); - } - MethodInfo manySubSelectAggMethod = null; - switch (exp3.Method.Name) //https://github.com/dotnetcore/FreeSql/issues/362 - { - case "Any": - case "Count": - fsqltables.Add(new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.Parent }); - fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); - var sql2 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { "1" })?.ToString(); - if (string.IsNullOrEmpty(sql2) == false) - manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectWhereSql, Expression.Constant($"exists({sql2.Replace(" \r\n", " \r\n ")})"), Expression.Constant(null)); - manySubSelectAggMethod = _dicExpressionLambdaToSqlAsSelectAggMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, _ => new ConcurrentDictionary()).GetOrAdd(exp3.Method.Name, exp3MethodName => - typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(parm123Ref.RefMiddleEntityType), parm123Ref.RefMiddleEntityType).GetMethod(exp3MethodName, new Type[0])); - manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectAggMethod); - break; - case "Sum": - case "Min": - case "Max": - case "Avg": - case "ToList": - case "ToOne": - case "First": - //解析:string.Join(",", w.Roles.AsSelect().ToList(b => b.RoleName) - var exp3Args0 = (exp3.Arguments[0] as UnaryExpression)?.Operand as LambdaExpression; - manySubSelectAggMethod = _dicSelectMethodToSql.GetOrAdd(fsqlType, fsqlType2 => - fsqlType2.GetMethods().Where(a => a.Name == "ToSql" && a.GetParameters().Length == 2 && a.GetParameters()[1].ParameterType == typeof(FieldAliasOptions) && a.GetGenericArguments().Length == 1).FirstOrDefault()); - if (manySubSelectAggMethod == null || exp3Args0 == null) throw new ArgumentException(CoreStrings.ManyToMany_AsSelect_NotSupport_Sum_Avg_etc); - manySubSelectAggMethod = manySubSelectAggMethod.MakeGenericMethod(exp3Args0.ReturnType); - var fsqls0p = fsql as Select0Provider; - var fsqls0pWhere = fsqls0p._where.ToString(); - fsqls0p._where.Clear(); - var fsqltablesLast = new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.InnerJoin }; - fsqltables.Add(fsqltablesLast); - fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); - fsqltablesLast.NavigateCondition = fsqls0p._where.ToString(); - if (fsqltablesLast.NavigateCondition.StartsWith(" AND (")) fsqltablesLast.NavigateCondition = fsqltablesLast.NavigateCondition.Substring(6, fsqltablesLast.NavigateCondition.Length - 7); - fsqls0p._where.Clear().Append(fsqls0pWhere); - var tsc3 = tsc.CloneDisableDiyParse(); - tsc3._tables = tsc._tables.ToList(); - var where2 = ExpressionLambdaToSql(Expression.Lambda(manySubSelectWhereExp, manySubSelectWhereParam), tsc3); - if (string.IsNullOrEmpty(where2) == false) fsqls0p._where.Append(" AND (").Append(where2).Append(")"); - - switch (exp3.Method.Name) - { - case "Sum": - case "Min": - case "Max": - case "Avg": - var map = new ReadAnonymousTypeInfo(); - var field = new StringBuilder(); - var index = -1; - - for (var a = 0; a < exp3Args0.Parameters.Count; a++) fsqls0p._tables[a].Parameter = exp3Args0.Parameters[a]; - ReadAnonymousField(fsqls0p._tables, field, map, ref index, exp3Args0, null, null, null, null, null, false); - var fieldSql = field.Length > 0 ? field.Remove(0, 2).ToString() : null; - - var sql4 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { $"{exp3.Method.Name.ToLower()}({fieldSql})" })?.ToString(); - asSelectBefores.Clear(); - return _common.IsNull($"({sql4.Replace(" \r\n", " \r\n ")})", formatSql(exp3.Method.ReturnType.CreateInstanceGetDefaultValue(), exp3.Method.ReturnType, null, null)); - } - - var sql3 = manySubSelectAggMethod.Invoke(fsql, new object[] { exp3Args0, FieldAliasOptions.AsProperty }) as string; - asSelectBefores.Clear(); - return $"({sql3.Replace(" \r\n", " \r\n ")})"; - } - asSelectBefores.Clear(); - return ExpressionLambdaToSql(manySubSelectExpBoy, tsc); - } - for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) - { - var col1 = parm123Ref.RefColumns[mn]; - var col2 = parm123Ref.Columns[mn]; - var pexp1 = Expression.Property(fsqlWhereParam, col1.CsName); - var pexp2 = Expression.Property(asSelectParentExp, col2.CsName); - if (col1.CsType != col2.CsType) - { - if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value"))); - if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value"))); + if (mn == 0) fsqlWhereExp = tmpExp; + else fsqlWhereExp = Expression.AndAlso(fsqlWhereExp, tmpExp); } - var tmpExp = Expression.Equal(pexp1, pexp2); - if (mn == 0) fsqlWhereExp = tmpExp; - else fsqlWhereExp = Expression.AndAlso(fsqlWhereExp, tmpExp); + fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlWhereExp, fsqlWhereParam) }); } - fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlWhereExp, fsqlWhereParam) }); } } asSelectBefores.Clear(); From b93a487e2f69fea00fa96c39aec6e2fe67cce5e1 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 15:17:46 +0800 Subject: [PATCH 16/20] add PgArrayToMany LazyLoding test #1145 --- FreeSql.DbContext/FreeSql.DbContext.xml | 9 -- .../PostgreSQLPgArrayToManyTest .cs | 120 ++++++++++++++++++ FreeSql/Internal/CommonExpression.cs | 2 +- FreeSql/Internal/UtilsExpressionTree.cs | 20 ++- 4 files changed, 134 insertions(+), 17 deletions(-) create mode 100644 FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 1ab5bf1cf..6b638cad4 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -795,14 +795,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs new file mode 100644 index 000000000..4ea569570 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs @@ -0,0 +1,120 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.PostgreSQL +{ + public class PostgreSQLPgArrayToManyTest + { + + [Table(Name = "pgarray_tomany_user_lazyloading")] + public class UserLazyLoading + { + public int Id { get; set; } + public int[] RoleIds { get; set; } + public string UserName { get; set; } + + [Navigate(nameof(RoleIds))] + public virtual List Roles { get; set; } + } + + [Table(Name = "pgarray_tomany_role_lazyloading")] + public class RoleLazyLoading + { + public int Id { get; set; } + public string RoleName { get; set; } + + [Navigate(nameof(UserLazyLoading.RoleIds))] + public virtual List Users { get; set; } + } + + [Fact] + public void LazyLoading() + { + var fsql = g.pgsql; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var roles = new[] + { + new RoleLazyLoading { Id = 1, RoleName = "role1" }, + new RoleLazyLoading { Id = 2, RoleName = "role2" }, + new RoleLazyLoading { Id = 3, RoleName = "role3" } + }; + Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); + var users = new[] + { + new UserLazyLoading { Id = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new UserLazyLoading { Id = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new UserLazyLoading { Id = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new UserLazyLoading { Id = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new UserLazyLoading { Id = 15, RoleIds = null, UserName = "user5" }, + new UserLazyLoading { Id = 16, RoleIds = new int[0], UserName = "user6" }, + }; + Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + + var role = fsql.Select().Where(a => a.Id == 1).First(); + Assert.IsNotType(role); + + var users2 = role.Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].Id); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].Id); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].Id); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].Id); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + var roles2 = users2[0].Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].Id); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].Id); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users2[1].Roles; + Assert.Equal(3, roles2.Count); + Assert.Equal(1, roles2[0].Id); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].Id); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].Id); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users2[2].Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].Id); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].Id); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users2[3].Roles; + Assert.Equal(3, roles2.Count); + Assert.Equal(3, roles2[0].Id); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].Id); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].Id); + Assert.Equal("role1", roles2[2].RoleName); + + var user = fsql.Select().Where(a => a.Id == 11).First(); + Assert.IsNotType(user); + + roles2 = user.Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].Id); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].Id); + Assert.Equal("role2", roles2[1].RoleName); + } + } +} diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index b7918b54e..30c34799a 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -2201,7 +2201,7 @@ void LocalSetSelectProviderAlias(string alias) midSelect.Where($"{midSelect._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.MiddleColumns[memberTbref.Columns.Count + tidx].Attribute.Name)} = {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[tidx].Attribute.Name)}"); for (var tidx = 0; tidx < memberTbref.Columns.Count; tidx++) midSelect.Where($"{midSelect._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.MiddleColumns[tidx].Attribute.Name)} = {mtmReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[tidx].Attribute.Name)}"); - select.Where($"exists({midSelect.ToSql("1").Replace(" \r\n", " \r\n ")})"); + select._where.Append($" AND exists({midSelect.ToSql("1").Replace(" \r\n", " \r\n ")})"); break; case TableRefType.OneToMany: var omtReftbname = e.FreeParse(Expression.MakeMemberAccess(memberExp.Expression, exp3Tb.Properties[exp3Tb.ColumnsByPosition[0].CsName])); diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 7e852d3be..5262560dc 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -969,9 +969,10 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo .Append(" if (base.").Append(pnv.Name).Append(" == null && __lazy__").Append(pnv.Name).AppendLine(" == false) {"); if (nvref.Exception == null) - cscode.Append(" base.").Append(pnv.Name).Append(" = __fsql_orm__.Select<").Append(propElementType.DisplayCsharp()) + cscode.Append(" var loc2 = __fsql_orm__.Select<").Append(propElementType.DisplayCsharp()) .Append(">().Where(a => __fsql_orm__.Select<").Append(tbmid.Type.DisplayCsharp()) .Append(">().Where(b => ").Append(lmbdWhere.ToString()).AppendLine(").Any()).ToList();") + .Append(" base.").Append(pnv.Name).Append(" = ").AppendLine(propTypeIsObservableCollection ? $"new ObservableCollection<{propElementType.DisplayCsharp()}>(loc2);" : "loc2;") .Append(" __lazy__").Append(pnv.Name).AppendLine(" = true;"); else cscode.Append(" throw new Exception(\"").Append(nvref.Exception.Message.Replace("\r\n", "\\r\\n").Replace("\"", "\\\"")).AppendLine("\");"); @@ -994,7 +995,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo { var isArrayToMany = false; var lmbdWhere = isLazy ? new StringBuilder() : null; - var cscodeExtLogic = ""; + var cscodeExtLogic1 = ""; + var cscodeExtLogic2 = ""; //Pgsql Array[] To Many if (common._orm.Ado.DataType == DataType.PostgreSQL) { @@ -1042,7 +1044,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - cscodeExtLogic = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; + cscodeExtLogic1 = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; + cscodeExtLogic2 = $" loc2 = this.{trycol.CsName}.Select(a => loc2.FirstOrDefault(b => b.{tbref.Primarys[0].CsName} == a)).ToList();"; lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName); if (trycol.CsType.GetElementType().IsNullableType() == false && tbref.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); lmbdWhere.Append(")"); @@ -1092,7 +1095,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) { lmbdWhere.Append(".Value"); - cscodeExtLogic = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + cscodeExtLogic1 = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; } lmbdWhere.Append(")"); nvref.Columns.Add(tbref.Primarys[0]); @@ -1219,12 +1222,14 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo if (vp?.Item2 == true) { //get 重写 cscode.Append(" ").Append(propGetModification).Append(" get {\r\n") - .Append(cscodeExtLogic) + .Append(cscodeExtLogic1) .Append(" if (base.").Append(pnv.Name).Append(" == null && __lazy__").Append(pnv.Name).AppendLine(" == false) {"); if (nvref.Exception == null) { - cscode.Append(" base.").Append(pnv.Name).Append(" = __fsql_orm__.Select<").Append(propElementType.DisplayCsharp()).Append(">().Where(a => ").Append(lmbdWhere.ToString()).AppendLine(").ToList();"); + cscode.Append(" var loc2 = __fsql_orm__.Select<").Append(propElementType.DisplayCsharp()).Append(">().Where(a => ").Append(lmbdWhere.ToString()).AppendLine(").ToList();") + .Append(cscodeExtLogic2) + .Append(" base.").Append(pnv.Name).Append(" = ").AppendLine(propTypeIsObservableCollection ? $"new ObservableCollection<{propElementType.DisplayCsharp()}>(loc2);" : "loc2;"); if (refprop != null) { cscode.Append(" foreach (var loc1 in base.").Append(pnv.Name).AppendLine(")") @@ -1381,7 +1386,8 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo .Append(" if (base.").Append(pnv.Name).Append(" == null && __lazy__").Append(pnv.Name).AppendLine(" == false) {"); if (nvref.Exception == null) - cscode.Append(" base.").Append(pnv.Name).Append(" = __fsql_orm__.Select<").Append(propTypeName).Append(">().Where(a => ").Append(lmbdWhere.ToString()).AppendLine(").ToOne();") + cscode.Append(" var loc3 = __fsql_orm__.Select<").Append(propTypeName).Append(">().Where(a => ").Append(lmbdWhere.ToString()).AppendLine(").ToOne();") + .Append(" base.").Append(pnv.Name).AppendLine(" = loc3;") .Append(" __lazy__").Append(pnv.Name).AppendLine(" = true;"); else cscode.Append(" throw new Exception(\"").Append(nvref.Exception.Message.Replace("\r\n", "\\r\\n").Replace("\"", "\\\"")).AppendLine("\");"); From 9376b54fdd041c2a689e7aa37646ee77627dff03 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 16:09:38 +0800 Subject: [PATCH 17/20] add PgArrayToMany IncludeMany test #1147 --- FreeSql.DbContext/FreeSql.DbContext.xml | 9 ++ .../PostgreSQLPgArrayToManyTest .cs | 113 +++++++++++++----- FreeSql/Internal/UtilsExpressionTree.cs | 14 +-- 3 files changed, 98 insertions(+), 38 deletions(-) diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 6b638cad4..1ab5bf1cf 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -795,5 +795,14 @@ + + + 批量注入 Repository,可以参考代码自行调整 + + + + + + diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs index 4ea569570..8c16b41b6 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs @@ -13,7 +13,8 @@ public class PostgreSQLPgArrayToManyTest [Table(Name = "pgarray_tomany_user_lazyloading")] public class UserLazyLoading { - public int Id { get; set; } + [Column(IsPrimary = true)] + public int UserId { get; set; } public int[] RoleIds { get; set; } public string UserName { get; set; } @@ -24,7 +25,8 @@ public class UserLazyLoading [Table(Name = "pgarray_tomany_role_lazyloading")] public class RoleLazyLoading { - public int Id { get; set; } + [Column(IsPrimary = true)] + public int RoleId { get; set; } public string RoleName { get; set; } [Navigate(nameof(UserLazyLoading.RoleIds))] @@ -40,80 +42,129 @@ public void LazyLoading() var roles = new[] { - new RoleLazyLoading { Id = 1, RoleName = "role1" }, - new RoleLazyLoading { Id = 2, RoleName = "role2" }, - new RoleLazyLoading { Id = 3, RoleName = "role3" } + new RoleLazyLoading { RoleId = 1, RoleName = "role1" }, + new RoleLazyLoading { RoleId = 2, RoleName = "role2" }, + new RoleLazyLoading { RoleId = 3, RoleName = "role3" } }; Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); var users = new[] { - new UserLazyLoading { Id = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, - new UserLazyLoading { Id = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, - new UserLazyLoading { Id = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, - new UserLazyLoading { Id = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, - new UserLazyLoading { Id = 15, RoleIds = null, UserName = "user5" }, - new UserLazyLoading { Id = 16, RoleIds = new int[0], UserName = "user6" }, + new UserLazyLoading { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new UserLazyLoading { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new UserLazyLoading { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new UserLazyLoading { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new UserLazyLoading { UserId = 15, RoleIds = null, UserName = "user5" }, + new UserLazyLoading { UserId = 16, RoleIds = new int[0], UserName = "user6" }, }; Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); - var role = fsql.Select().Where(a => a.Id == 1).First(); + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + Assert.Equal(3, roles3.Count); + var roles2 = roles3; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + var users2 = roles2[0].Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + users2 = roles2[1].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + users2 = roles2[2].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(12, users2[0].UserId); + Assert.Equal("user2", users2[0].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[0].RoleIds)); + Assert.Equal(13, users2[1].UserId); + Assert.Equal("user3", users2[1].UserName); + Assert.Equal("1,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + var role = fsql.Select().Where(a => a.RoleId == 1).First(); Assert.IsNotType(role); - var users2 = role.Users; + users2 = role.Users; Assert.Equal(4, users2.Count); - Assert.Equal(11, users2[0].Id); + Assert.Equal(11, users2[0].UserId); Assert.Equal("user1", users2[0].UserName); Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); - Assert.Equal(12, users2[1].Id); + Assert.Equal(12, users2[1].UserId); Assert.Equal("user2", users2[1].UserName); Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); - Assert.Equal(13, users2[2].Id); + Assert.Equal(13, users2[2].UserId); Assert.Equal("user3", users2[2].UserName); Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); - Assert.Equal(14, users2[3].Id); + Assert.Equal(14, users2[3].UserId); Assert.Equal("user4", users2[3].UserName); Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); - var roles2 = users2[0].Roles; + roles2 = users2[0].Roles; Assert.Equal(2, roles2.Count); - Assert.Equal(1, roles2[0].Id); + Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); - Assert.Equal(2, roles2[1].Id); + Assert.Equal(2, roles2[1].RoleId); Assert.Equal("role2", roles2[1].RoleName); roles2 = users2[1].Roles; Assert.Equal(3, roles2.Count); - Assert.Equal(1, roles2[0].Id); + Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); - Assert.Equal(2, roles2[1].Id); + Assert.Equal(2, roles2[1].RoleId); Assert.Equal("role2", roles2[1].RoleName); - Assert.Equal(3, roles2[2].Id); + Assert.Equal(3, roles2[2].RoleId); Assert.Equal("role3", roles2[2].RoleName); roles2 = users2[2].Roles; Assert.Equal(2, roles2.Count); - Assert.Equal(1, roles2[0].Id); + Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); - Assert.Equal(3, roles2[1].Id); + Assert.Equal(3, roles2[1].RoleId); Assert.Equal("role3", roles2[1].RoleName); roles2 = users2[3].Roles; Assert.Equal(3, roles2.Count); - Assert.Equal(3, roles2[0].Id); + Assert.Equal(3, roles2[0].RoleId); Assert.Equal("role3", roles2[0].RoleName); - Assert.Equal(2, roles2[1].Id); + Assert.Equal(2, roles2[1].RoleId); Assert.Equal("role2", roles2[1].RoleName); - Assert.Equal(1, roles2[2].Id); + Assert.Equal(1, roles2[2].RoleId); Assert.Equal("role1", roles2[2].RoleName); - var user = fsql.Select().Where(a => a.Id == 11).First(); + var user = fsql.Select().Where(a => a.UserId == 11).First(); Assert.IsNotType(user); roles2 = user.Roles; Assert.Equal(2, roles2.Count); - Assert.Equal(1, roles2[0].Id); + Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); - Assert.Equal(2, roles2[1].Id); + Assert.Equal(2, roles2[1].RoleId); Assert.Equal("role2", roles2[1].RoleName); } } diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 5262560dc..35fff5825 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -1028,13 +1028,13 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo if (pnvBind == null && trycol == null) { var findtbrefPkCsName = tbref.Primarys[0].CsName.TrimStart('_'); - if (findtbrefPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtbrefPkCsName = findtbrefPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); + if (findtbrefPkCsName.StartsWith(tbref.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtbrefPkCsName = findtbrefPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); var findtrytb = pnv.Name; if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); findtrytb += tbref.CsName; if ( - tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtbrefPkCsName}", out trycol) == false && //骆峰命名 - tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtbrefPkCsName}", out trycol) == false //下划线命名 + trytb.ColumnsByCs.TryGetValue($"{findtrytb}{findtbrefPkCsName}s", out trycol) == false && //骆峰命名 + trytb.ColumnsByCs.TryGetValue($"{findtrytb}_{findtbrefPkCsName}s", out trycol) == false //下划线命名 ) { } @@ -1077,11 +1077,11 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo var findtrytbPkCsName = trytb.Primarys[0].CsName.TrimStart('_'); if (findtrytbPkCsName.StartsWith(trytb.Type.Name, StringComparison.CurrentCultureIgnoreCase)) findtrytbPkCsName = findtrytbPkCsName.Substring(trytb.Type.Name.Length).TrimStart('_'); var findtrytb = pnv.Name; - if (findtrytb.EndsWith($"{tbref.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - tbref.CsName.Length - 1); + if (findtrytb.EndsWith($"{trytb.CsName}s", StringComparison.CurrentCultureIgnoreCase)) findtrytb = findtrytb.Substring(0, findtrytb.Length - trytb.CsName.Length - 1); findtrytb += trytb.CsName; if ( - tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}", out trycol) == false && //骆峰命名 - tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}", out trycol) == false //下划线命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}{findtrytbPkCsName}s", out trycol) == false && //骆峰命名 + tbref.ColumnsByCs.TryGetValue($"{findtrytb}_{findtrytbPkCsName}s", out trycol) == false //下划线命名 ) { } @@ -1098,7 +1098,7 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo cscodeExtLogic1 = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; } lmbdWhere.Append(")"); - nvref.Columns.Add(tbref.Primarys[0]); + nvref.Columns.Add(trytb.Primarys[0]); nvref.RefColumns.Add(trycol); nvref.RefEntityType = tbref.Type; nvref.RefType = TableRefType.PgArrayToMany; From 24d072409d05e93894978aa552cb25e29bf66a8f Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 16:22:49 +0800 Subject: [PATCH 18/20] add PgArrayToMany IncludeMany test2 #1147 --- .../PostgreSQLPgArrayToManyTest .cs | 57 ++++++++++++++++++- .../SelectProvider/Select1Provider.cs | 6 +- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs index 8c16b41b6..fac2fbfbe 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs @@ -58,9 +58,62 @@ public void LazyLoading() }; Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + Assert.Equal(6, users3.Count); + var users2 = users3; + Assert.Equal(11, users2[0].UserId); + Assert.Equal(12, users2[1].UserId); + Assert.Equal(13, users2[2].UserId); + Assert.Equal(14, users2[3].UserId); + Assert.Equal(15, users2[4].UserId); + Assert.Equal(16, users2[5].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("user5", users2[4].UserName); + Assert.Equal("user6", users2[5].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + Assert.Null(users2[4].RoleIds); + Assert.Empty(users2[5].RoleIds); + + var roles2 = users3[0].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users3[1].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users3[2].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users3[3].Roles; + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + Assert.Null(users3[4].Roles); + Assert.Empty(users3[5].Roles); + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); Assert.Equal(3, roles3.Count); - var roles2 = roles3; + roles2 = roles3; Assert.Equal(1, roles2[0].RoleId); Assert.Equal("role1", roles2[0].RoleName); Assert.Equal(2, roles2[1].RoleId); @@ -68,7 +121,7 @@ public void LazyLoading() Assert.Equal(3, roles2[2].RoleId); Assert.Equal("role3", roles2[2].RoleName); - var users2 = roles2[0].Users; + users2 = roles2[0].Users; Assert.Equal(4, users2.Count); Assert.Equal(11, users2[0].UserId); Assert.Equal("user1", users2[0].UserName); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 838e01ebf..348e21ebf 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -1184,11 +1184,12 @@ public ISelect IncludeMany(Expression { var arrVal = getListValue(a, tbref.Columns[0].CsName, 0) as Array; + if (arrVal == null) return null; var arrObjVal = new object[arrVal.Length]; arrVal.CopyTo(arrObjVal, 0); return arrObjVal; - }).Where(a => a != null).ToArray(); - var arrExp = Expression.NewArrayInit(tbref.RefColumns[0].CsType, listKeys.SelectMany(a => a).Distinct() + }).ToArray(); + var arrExp = Expression.NewArrayInit(tbref.RefColumns[0].CsType, listKeys.Where(a => a != null).SelectMany(a => a).Distinct() .Select(a => Expression.Constant(Utils.GetDataReaderValue(tbref.RefColumns[0].CsType, a), tbref.RefColumns[0].CsType)).ToArray()); var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); var containsMethod = _dicTypeMethod.GetOrAdd(tbref.RefColumns[0].CsType, et => new ConcurrentDictionary()).GetOrAdd("Contains", mn => @@ -1235,6 +1236,7 @@ public ISelect IncludeMany(Expression(); foreach (var dlk in dicListKeys) { From 1a0f0d0f02242d6681726dee8cbd3393c2abdf3a Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 19:20:59 +0800 Subject: [PATCH 19/20] add PgArrayToMany IncludeMany Dto test #1147 --- .../PostgreSQL/PostgreSQLPgArrayToManyTest .cs | 11 +++++++++++ .../CommonProvider/SelectProvider/Select1Provider.cs | 11 +++++++++++ FreeSql/Internal/Model/TableInfo.cs | 3 ++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs index fac2fbfbe..2b7f32c59 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs @@ -58,6 +58,17 @@ public void LazyLoading() }; Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + { + user = a, roles = a.Roles + }); + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + { + role = a, + users = a.Users + }); + + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); Assert.Equal(6, users3.Count); var users2 = users3; diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 348e21ebf..7f8a81d49 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -222,6 +222,17 @@ MemberInitExpression GetIncludeManyNewInitExpression(IncludeManyNewInit imni) var bindings = new List(); if (imni.IsOutputPrimary) bindings.AddRange(imni.Table.Primarys.Select(a => Expression.Bind(imni.Table.Properties[a.CsName], Expression.MakeMemberAccess(imni.CurrentExpression, imni.Table.Properties[a.CsName])))); if (imni.Childs.Any()) bindings.AddRange(imni.Childs.Select(a => Expression.Bind(imni.Table.Properties[a.Key], GetIncludeManyNewInitExpression(a.Value)))); + var pgarrayToManys = imni.Table.GetAllTableRef().Select(tr => + { + if (tr.Value.RefType != TableRefType.PgArrayToMany) return null; + var reftb = _orm.CodeFirst.GetTableByEntity(tr.Value.RefEntityType); + if (tr.Value.RefColumns[0] == reftb.Primarys[0]) + { + bindings.Add(Expression.Bind(imni.Table.Properties[tr.Value.Columns[0].CsName], Expression.MakeMemberAccess(imni.CurrentExpression, imni.Table.Properties[tr.Value.Columns[0].CsName]))); + return tr.Key; + } + return null; + }).ToList(); return Expression.MemberInit(imni.Table.Type.InternalNewExpression(), bindings); } diff --git a/FreeSql/Internal/Model/TableInfo.cs b/FreeSql/Internal/Model/TableInfo.cs index 6c8583149..13555dfd2 100644 --- a/FreeSql/Internal/Model/TableInfo.cs +++ b/FreeSql/Internal/Model/TableInfo.cs @@ -48,6 +48,7 @@ public TableRef GetTableRef(string propertyName, bool isThrowException) } return tryref; } + public IEnumerable> GetAllTableRef() => _refs; //public void CopyTo(TableInfo target) //{ @@ -79,7 +80,7 @@ public TableRef GetTableRef(string propertyName, bool isThrowException) // target.VersionColumn = getOrCloneColumn(this.VersionColumn); // foreach (var rf in this._refs) target._refs.TryAdd(rf.Key, new TableRef // { - + // }); From 53c2793e5ea15b3d9700ec983a0bc9fd5f34a842 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 6 Jun 2022 21:09:43 +0800 Subject: [PATCH 20/20] add PgArrayToMany ObservableCollection test #1145 --- FreeSql.DbContext/FreeSql.DbContext.xml | 9 - .../LazyLoadingTest .cs} | 92 ++++-- .../PostgreSQL/PgArrayToMany/NormalTest.cs | 221 ++++++++++++++ .../ObservableCollectionLazyLoadingTest.cs | 275 ++++++++++++++++++ .../PgArrayToMany/ObservableCollectionTest.cs | 223 ++++++++++++++ FreeSql/FreeSql.xml | 183 ++++++++++++ FreeSql/Internal/CommonExpression.cs | 32 +- FreeSql/Internal/UtilsExpressionTree.cs | 26 +- 8 files changed, 1010 insertions(+), 51 deletions(-) rename FreeSql.Tests/FreeSql.Tests/PostgreSQL/{PostgreSQLPgArrayToManyTest .cs => PgArrayToMany/LazyLoadingTest .cs} (68%) create mode 100644 FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/NormalTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionLazyLoadingTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionTest.cs diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 1ab5bf1cf..6b638cad4 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -795,14 +795,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/LazyLoadingTest .cs similarity index 68% rename from FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs rename to FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/LazyLoadingTest .cs index 2b7f32c59..834d83b8b 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PostgreSQLPgArrayToManyTest .cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/LazyLoadingTest .cs @@ -1,17 +1,18 @@ using FreeSql.DataAnnotations; using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Text; using Xunit; -namespace FreeSql.Tests.PostgreSQL +namespace FreeSql.Tests.PostgreSQL.PgArrayToMany { - public class PostgreSQLPgArrayToManyTest + public class LazyLoadingTest { [Table(Name = "pgarray_tomany_user_lazyloading")] - public class UserLazyLoading + public class User { [Column(IsPrimary = true)] public int UserId { get; set; } @@ -19,57 +20,96 @@ public class UserLazyLoading public string UserName { get; set; } [Navigate(nameof(RoleIds))] - public virtual List Roles { get; set; } + public virtual List Roles { get; set; } } [Table(Name = "pgarray_tomany_role_lazyloading")] - public class RoleLazyLoading + public class Role { [Column(IsPrimary = true)] public int RoleId { get; set; } public string RoleName { get; set; } - [Navigate(nameof(UserLazyLoading.RoleIds))] - public virtual List Users { get; set; } + [Navigate(nameof(User.RoleIds))] + public virtual List Users { get; set; } } [Fact] public void LazyLoading() { var fsql = g.pgsql; - fsql.Delete().Where("1=1").ExecuteAffrows(); - fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); var roles = new[] { - new RoleLazyLoading { RoleId = 1, RoleName = "role1" }, - new RoleLazyLoading { RoleId = 2, RoleName = "role2" }, - new RoleLazyLoading { RoleId = 3, RoleName = "role3" } + new Role { RoleId = 1, RoleName = "role1" }, + new Role { RoleId = 2, RoleName = "role2" }, + new Role { RoleId = 3, RoleName = "role3" } }; Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); var users = new[] { - new UserLazyLoading { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, - new UserLazyLoading { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, - new UserLazyLoading { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, - new UserLazyLoading { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, - new UserLazyLoading { UserId = 15, RoleIds = null, UserName = "user5" }, - new UserLazyLoading { UserId = 16, RoleIds = new int[0], UserName = "user6" }, + new User { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new User { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new User { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new User { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new User { UserId = 15, RoleIds = null, UserName = "user5" }, + new User { UserId = 16, RoleIds = new int[0], UserName = "user6" }, }; Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); - var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + var users5Select = fsql.Select().Where(a => a.Roles.Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_lazyloading"" b + WHERE (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) AND (b.""rolename"" = 'role1') + limit 1))", users5Select.ToSql()); + var users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + users5Select = fsql.Select().Where(a => a.Roles.AsSelect().Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_lazyloading"" b + WHERE (b.""rolename"" = 'role1') AND (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) + limit 1))", users5Select.ToSql()); + users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + + var roles5Select = fsql.Select().Where(a => a.Users.Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_lazyloading"" b + WHERE (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) AND (b.""username"" = 'user1') + limit 1))", roles5Select.ToSql()); + var roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + roles5Select = fsql.Select().Where(a => a.Users.AsSelect().Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_lazyloading"" b + WHERE (b.""username"" = 'user1') AND (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) + limit 1))", roles5Select.ToSql()); + roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + + + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new { user = a, roles = a.Roles }); - var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new { role = a, users = a.Users }); - var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); Assert.Equal(6, users3.Count); var users2 = users3; Assert.Equal(11, users2[0].UserId); @@ -122,7 +162,7 @@ public void LazyLoading() Assert.Null(users3[4].Roles); Assert.Empty(users3[5].Roles); - var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); Assert.Equal(3, roles3.Count); roles2 = roles3; Assert.Equal(1, roles2[0].RoleId); @@ -171,8 +211,8 @@ public void LazyLoading() Assert.Equal("user4", users2[2].UserName); Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); - var role = fsql.Select().Where(a => a.RoleId == 1).First(); - Assert.IsNotType(role); + var role = fsql.Select().Where(a => a.RoleId == 1).First(); + Assert.IsNotType(role); users2 = role.Users; Assert.Equal(4, users2.Count); @@ -221,8 +261,8 @@ public void LazyLoading() Assert.Equal(1, roles2[2].RoleId); Assert.Equal("role1", roles2[2].RoleName); - var user = fsql.Select().Where(a => a.UserId == 11).First(); - Assert.IsNotType(user); + var user = fsql.Select().Where(a => a.UserId == 11).First(); + Assert.IsNotType(user); roles2 = user.Roles; Assert.Equal(2, roles2.Count); diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/NormalTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/NormalTest.cs new file mode 100644 index 000000000..a697dc625 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/NormalTest.cs @@ -0,0 +1,221 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.PostgreSQL.PgArrayToMany +{ + public class NormalTest + { + + [Table(Name = "pgarray_tomany_user_normal")] + public class User + { + [Column(IsPrimary = true)] + public int UserId { get; set; } + public int[] RoleIds { get; set; } + public string UserName { get; set; } + + [Navigate(nameof(RoleIds))] + public List Roles { get; set; } + } + + [Table(Name = "pgarray_tomany_role_normal")] + public class Role + { + [Column(IsPrimary = true)] + public int RoleId { get; set; } + public string RoleName { get; set; } + + [Navigate(nameof(User.RoleIds))] + public List Users { get; set; } + } + + [Fact] + public void Normal() + { + var fsql = g.pgsql; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var roles = new[] + { + new Role { RoleId = 1, RoleName = "role1" }, + new Role { RoleId = 2, RoleName = "role2" }, + new Role { RoleId = 3, RoleName = "role3" } + }; + Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); + var users = new[] + { + new User { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new User { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new User { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new User { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new User { UserId = 15, RoleIds = null, UserName = "user5" }, + new User { UserId = 16, RoleIds = new int[0], UserName = "user6" }, + }; + Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + + var users5Select = fsql.Select().Where(a => a.Roles.Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_normal"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_normal"" b + WHERE (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) AND (b.""rolename"" = 'role1') + limit 1))", users5Select.ToSql()); + var users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + users5Select = fsql.Select().Where(a => a.Roles.AsSelect().Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_normal"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_normal"" b + WHERE (b.""rolename"" = 'role1') AND (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) + limit 1))", users5Select.ToSql()); + users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + + var roles5Select = fsql.Select().Where(a => a.Users.Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_normal"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_normal"" b + WHERE (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) AND (b.""username"" = 'user1') + limit 1))", roles5Select.ToSql()); + var roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + roles5Select = fsql.Select().Where(a => a.Users.AsSelect().Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_normal"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_normal"" b + WHERE (b.""username"" = 'user1') AND (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) + limit 1))", roles5Select.ToSql()); + roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + + + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + { + user = a, roles = a.Roles + }); + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + { + role = a, + users = a.Users + }); + + + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + Assert.Equal(6, users3.Count); + var users2 = users3; + Assert.Equal(11, users2[0].UserId); + Assert.Equal(12, users2[1].UserId); + Assert.Equal(13, users2[2].UserId); + Assert.Equal(14, users2[3].UserId); + Assert.Equal(15, users2[4].UserId); + Assert.Equal(16, users2[5].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("user5", users2[4].UserName); + Assert.Equal("user6", users2[5].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + Assert.Null(users2[4].RoleIds); + Assert.Empty(users2[5].RoleIds); + + var roles2 = users3[0].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users3[1].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users3[2].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users3[3].Roles; + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + Assert.Null(users3[4].Roles); + Assert.Empty(users3[5].Roles); + + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + Assert.Equal(3, roles3.Count); + roles2 = roles3; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + users2 = roles2[0].Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + users2 = roles2[1].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + users2 = roles2[2].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(12, users2[0].UserId); + Assert.Equal("user2", users2[0].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[0].RoleIds)); + Assert.Equal(13, users2[1].UserId); + Assert.Equal("user3", users2[1].UserName); + Assert.Equal("1,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + var role = fsql.Select().Where(a => a.RoleId == 1).First(); + Assert.IsType(role); + + var user = fsql.Select().Where(a => a.UserId == 11).First(); + Assert.IsType(user); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionLazyLoadingTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionLazyLoadingTest.cs new file mode 100644 index 000000000..29d9d271b --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionLazyLoadingTest.cs @@ -0,0 +1,275 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.PostgreSQL.PgArrayToMany +{ + public class ObservableCollectionLazyLoadingTest + { + + [Table(Name = "pgarray_tomany_user_observablecollection_lazyloading")] + public class User + { + [Column(IsPrimary = true)] + public int UserId { get; set; } + public int[] RoleIds { get; set; } + public string UserName { get; set; } + + [Navigate(nameof(RoleIds))] + public virtual List Roles { get; set; } + } + + [Table(Name = "pgarray_tomany_role_observablecollection_lazyloading")] + public class Role + { + [Column(IsPrimary = true)] + public int RoleId { get; set; } + public string RoleName { get; set; } + + [Navigate(nameof(User.RoleIds))] + public virtual List Users { get; set; } + } + + [Fact] + public void ObservableCollectionLazyLoading() + { + var fsql = g.pgsql; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var roles = new[] + { + new Role { RoleId = 1, RoleName = "role1" }, + new Role { RoleId = 2, RoleName = "role2" }, + new Role { RoleId = 3, RoleName = "role3" } + }; + Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); + var users = new[] + { + new User { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new User { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new User { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new User { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new User { UserId = 15, RoleIds = null, UserName = "user5" }, + new User { UserId = 16, RoleIds = new int[0], UserName = "user6" }, + }; + Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + + var users5Select = fsql.Select().Where(a => a.Roles.Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_observablecollection_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_observablecollection_lazyloading"" b + WHERE (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) AND (b.""rolename"" = 'role1') + limit 1))", users5Select.ToSql()); + var users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + users5Select = fsql.Select().Where(a => a.Roles.AsSelect().Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_observablecollection_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_observablecollection_lazyloading"" b + WHERE (b.""rolename"" = 'role1') AND (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) + limit 1))", users5Select.ToSql()); + users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + + var roles5Select = fsql.Select().Where(a => a.Users.Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_observablecollection_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_observablecollection_lazyloading"" b + WHERE (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) AND (b.""username"" = 'user1') + limit 1))", roles5Select.ToSql()); + var roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + roles5Select = fsql.Select().Where(a => a.Users.AsSelect().Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_observablecollection_lazyloading"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_observablecollection_lazyloading"" b + WHERE (b.""username"" = 'user1') AND (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) + limit 1))", roles5Select.ToSql()); + roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + + + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + { + user = a, roles = a.Roles + }); + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + { + role = a, + users = a.Users + }); + + + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + Assert.Equal(6, users3.Count); + var users2 = users3; + Assert.Equal(11, users2[0].UserId); + Assert.Equal(12, users2[1].UserId); + Assert.Equal(13, users2[2].UserId); + Assert.Equal(14, users2[3].UserId); + Assert.Equal(15, users2[4].UserId); + Assert.Equal(16, users2[5].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("user5", users2[4].UserName); + Assert.Equal("user6", users2[5].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + Assert.Null(users2[4].RoleIds); + Assert.Empty(users2[5].RoleIds); + + var roles2 = users3[0].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users3[1].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users3[2].Roles; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users3[3].Roles; + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + Assert.Null(users3[4].Roles); + Assert.Empty(users3[5].Roles); + + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + Assert.Equal(3, roles3.Count); + roles2 = roles3; + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + users2 = roles2[0].Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + users2 = roles2[1].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + users2 = roles2[2].Users; + Assert.Equal(3, users2.Count); + Assert.Equal(12, users2[0].UserId); + Assert.Equal("user2", users2[0].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[0].RoleIds)); + Assert.Equal(13, users2[1].UserId); + Assert.Equal("user3", users2[1].UserName); + Assert.Equal("1,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + var role = fsql.Select().Where(a => a.RoleId == 1).First(); + Assert.IsNotType(role); + + users2 = role.Users; + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + roles2 = users2[0].Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users2[1].Roles; + Assert.Equal(3, roles2.Count); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users2[2].Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users2[3].Roles; + Assert.Equal(3, roles2.Count); + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + var user = fsql.Select().Where(a => a.UserId == 11).First(); + Assert.IsNotType(user); + + roles2 = user.Roles; + Assert.Equal(2, roles2.Count); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionTest.cs new file mode 100644 index 000000000..671dadca4 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/PgArrayToMany/ObservableCollectionTest.cs @@ -0,0 +1,223 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.PostgreSQL.PgArrayToMany +{ + public class ObservableCollectionTest + { + + [Table(Name = "pgarray_tomany_user_observablecollection")] + public class User + { + [Column(IsPrimary = true)] + public int UserId { get; set; } + public int[] RoleIds { get; set; } + public string UserName { get; set; } + + [Navigate(nameof(RoleIds))] + public ObservableCollection Roles { get; set; } + } + + [Table(Name = "pgarray_tomany_role_observablecollection")] + public class Role + { + [Column(IsPrimary = true)] + public int RoleId { get; set; } + public string RoleName { get; set; } + + [Navigate(nameof(User.RoleIds))] + public ObservableCollection Users { get; set; } + } + + [Fact] + public void ObservableCollection() + { + var fsql = g.pgsql; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var roles = new[] + { + new Role { RoleId = 1, RoleName = "role1" }, + new Role { RoleId = 2, RoleName = "role2" }, + new Role { RoleId = 3, RoleName = "role3" } + }; + Assert.Equal(3, fsql.Insert(roles).ExecuteAffrows()); + var users = new[] + { + new User { UserId = 11, RoleIds = new [] { 1,2 }, UserName = "user1" }, + new User { UserId = 12, RoleIds = new [] { 1,2,3 }, UserName = "user2" }, + new User { UserId = 13, RoleIds = new [] { 1,3 }, UserName = "user3" }, + new User { UserId = 14, RoleIds = new [] { 3,2,1 }, UserName = "user4" }, + new User { UserId = 15, RoleIds = null, UserName = "user5" }, + new User { UserId = 16, RoleIds = new int[0], UserName = "user6" }, + }; + Assert.Equal(6, fsql.Insert(users).ExecuteAffrows()); + + var users5Select = fsql.Select().Where(a => a.Roles.Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_observablecollection"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_observablecollection"" b + WHERE (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) AND (b.""rolename"" = 'role1') + limit 1))", users5Select.ToSql()); + var users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + users5Select = fsql.Select().Where(a => a.Roles.AsSelect().Any(b => b.RoleName == "role1")); + Assert.Equal(@"SELECT a.""userid"", a.""roleids"", a.""username"" +FROM ""pgarray_tomany_user_observablecollection"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_role_observablecollection"" b + WHERE (b.""rolename"" = 'role1') AND (a.""roleids"" @> ARRAY[b.""roleid""]::int4[]) + limit 1))", users5Select.ToSql()); + users5 = users5Select.ToList(); + Assert.Equal(4, users5.Count); + + var roles5Select = fsql.Select().Where(a => a.Users.Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_observablecollection"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_observablecollection"" b + WHERE (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) AND (b.""username"" = 'user1') + limit 1))", roles5Select.ToSql()); + var roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + roles5Select = fsql.Select().Where(a => a.Users.AsSelect().Any(b => b.UserName == "user1")); + Assert.Equal(@"SELECT a.""roleid"", a.""rolename"" +FROM ""pgarray_tomany_role_observablecollection"" a +WHERE (exists(SELECT 1 + FROM ""pgarray_tomany_user_observablecollection"" b + WHERE (b.""username"" = 'user1') AND (b.""roleids"" @> ARRAY[a.""roleid""]::int4[]) + limit 1))", roles5Select.ToSql()); + roles5 = roles5Select.ToList(); + Assert.Equal(2, roles5.Count); + + + var users4 = fsql.Select().IncludeMany(a => a.Roles).ToList(a => new + { + user = a, + roles = a.Roles + }); + var roles4 = fsql.Select().IncludeMany(a => a.Users).ToList(a => new + { + role = a, + users = a.Users + }); + + + var users3 = fsql.Select().IncludeMany(a => a.Roles).ToList(); + Assert.Equal(6, users3.Count); + var users2 = users3; + Assert.Equal(11, users2[0].UserId); + Assert.Equal(12, users2[1].UserId); + Assert.Equal(13, users2[2].UserId); + Assert.Equal(14, users2[3].UserId); + Assert.Equal(15, users2[4].UserId); + Assert.Equal(16, users2[5].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("user5", users2[4].UserName); + Assert.Equal("user6", users2[5].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + Assert.Null(users2[4].RoleIds); + Assert.Empty(users2[5].RoleIds); + + var roles2 = users3[0].Roles.ToList(); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + + roles2 = users3[1].Roles.ToList(); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + roles2 = users3[2].Roles.ToList(); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(3, roles2[1].RoleId); + Assert.Equal("role3", roles2[1].RoleName); + + roles2 = users3[3].Roles.ToList(); + Assert.Equal(3, roles2[0].RoleId); + Assert.Equal("role3", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(1, roles2[2].RoleId); + Assert.Equal("role1", roles2[2].RoleName); + + Assert.Null(users3[4].Roles); + Assert.Empty(users3[5].Roles); + + var roles3 = fsql.Select().IncludeMany(a => a.Users).ToList(); + Assert.Equal(3, roles3.Count); + roles2 = roles3.ToList(); + Assert.Equal(1, roles2[0].RoleId); + Assert.Equal("role1", roles2[0].RoleName); + Assert.Equal(2, roles2[1].RoleId); + Assert.Equal("role2", roles2[1].RoleName); + Assert.Equal(3, roles2[2].RoleId); + Assert.Equal("role3", roles2[2].RoleName); + + users2 = roles2[0].Users.ToList(); + Assert.Equal(4, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(13, users2[2].UserId); + Assert.Equal("user3", users2[2].UserName); + Assert.Equal("1,3", string.Join(",", users2[2].RoleIds)); + Assert.Equal(14, users2[3].UserId); + Assert.Equal("user4", users2[3].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[3].RoleIds)); + + users2 = roles2[1].Users.ToList(); + Assert.Equal(3, users2.Count); + Assert.Equal(11, users2[0].UserId); + Assert.Equal("user1", users2[0].UserName); + Assert.Equal("1,2", string.Join(",", users2[0].RoleIds)); + Assert.Equal(12, users2[1].UserId); + Assert.Equal("user2", users2[1].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + users2 = roles2[2].Users.ToList(); + Assert.Equal(3, users2.Count); + Assert.Equal(12, users2[0].UserId); + Assert.Equal("user2", users2[0].UserName); + Assert.Equal("1,2,3", string.Join(",", users2[0].RoleIds)); + Assert.Equal(13, users2[1].UserId); + Assert.Equal("user3", users2[1].UserName); + Assert.Equal("1,3", string.Join(",", users2[1].RoleIds)); + Assert.Equal(14, users2[2].UserId); + Assert.Equal("user4", users2[2].UserName); + Assert.Equal("3,2,1", string.Join(",", users2[2].RoleIds)); + + var role = fsql.Select().Where(a => a.RoleId == 1).First(); + Assert.IsType(role); + + var user = fsql.Select().Where(a => a.UserId == 11).First(); + Assert.IsType(user); + } + } +} diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 160a7397d..434ec7a25 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -3303,6 +3303,177 @@ + + + 测试数据库是否连接正确,本方法执行如下命令: + MySql/SqlServer/PostgreSQL/达梦/人大金仓/神通: SELECT 1 + Oracle: SELECT 1 FROM dual + + 命令超时设置(秒) + + true: 成功, false: 失败 + + + + 查询,若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】 + + + + + + + + + + 查询,ExecuteReaderAsync(dr => {}, "select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 查询 + + + + + + + + + 查询,ExecuteArrayAsync("select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 查询 + + + + + + + + + 查询,ExecuteDataSetAsync("select * from user where age > @age; select 2", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 查询 + + + + + + + + + 查询,ExecuteDataTableAsync("select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 在【主库】执行 + + + + + + + + + 在【主库】执行,ExecuteNonQueryAsync("delete from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 在【主库】执行 + + + + + + + + + 在【主库】执行,ExecuteScalarAsync("select 1 from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new SqlParameter { ParameterName = "age", Value = 25 }) + + + + + + + + + + + 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + + 执行SQL返回对象集合,Query<User>("select * from user where age > @age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 }) + + + + + + + + + + + + 执行SQL返回对象集合,Query<User, Address>("select * from user where age > @age; select * from address", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + 可自定义解析表达式 @@ -4326,6 +4497,12 @@ 超时 + + + 获取资源 + + + 使用完毕后,归还资源 @@ -4401,6 +4578,12 @@ 资源对象 + + + 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 + + 资源对象 + 归还对象给对象池的时候触发 diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 30c34799a..9b22e35d0 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -1212,9 +1212,19 @@ public string ExpressionLambdaToSql(Expression exp, ExpTSC tsc) var amtReftbname = ExpressionLambdaToSql(Expression.MakeMemberAccess(asSelectParentExp, parm123Tb.Properties[parm123Tb.ColumnsByPosition[0].CsName]), tsc); amtReftbname = amtReftbname.Substring(0, amtReftbname.Length - _common.QuoteSqlName(parm123Tb.ColumnsByPosition[0].Attribute.Name).Length - 1); if (parm123Ref.RefColumns[0] == fsqltables[0].Table.Primarys[0]) - (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)} @> {fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)}").Append(")"); - else if (parm123Ref.Columns[0] == fsqltables[0].Table.Primarys[0]) - (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)} @> {fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)}").Append(")"); + { + var dbinfo = _common._orm.CodeFirst.GetDbInfo(parm123Ref.Columns[0].CsType); + (fsql as Select0Provider)._where.Append(" AND (").Append($"{amtReftbname}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)} @> ARRAY[{fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)}]::{dbinfo?.dbtype}").Append(")"); + } + else if (parm123Ref.Columns[0] == parm123Tb.Primarys[0]) + { + var dbinfo = _common._orm.CodeFirst.GetDbInfo(parm123Ref.RefColumns[0].CsType); + (fsql as Select0Provider)._where.Append(" AND (").Append($"{fsqltables[0].Alias}.{_common.QuoteSqlName(parm123Ref.RefColumns[0].Attribute.Name)} @> ARRAY[{amtReftbname}.{_common.QuoteSqlName(parm123Ref.Columns[0].Attribute.Name)}]::{dbinfo?.dbtype}").Append(")"); + } + else + { + ; + } } else { @@ -2213,9 +2223,19 @@ void LocalSetSelectProviderAlias(string alias) var amtReftbname = e.FreeParse(Expression.MakeMemberAccess(memberExp.Expression, exp3Tb.Properties[exp3Tb.ColumnsByPosition[0].CsName])); amtReftbname = amtReftbname.Substring(0, amtReftbname.Length - commonExp._common.QuoteSqlName(exp3Tb.ColumnsByPosition[0].Attribute.Name).Length - 1); if (memberTbref.RefColumns[0] == select._tables[0].Table.Primarys[0]) - select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)} @> {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)}"); - else if (memberTbref.Columns[0] == select._tables[0].Table.Primarys[0]) - select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)} @> {select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)}"); + { + var dbinfo = commonExp._common._orm.CodeFirst.GetDbInfo(memberTbref.Columns[0].CsType); + select.Where($"{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)} @> ARRAY[{select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)}]::{dbinfo?.dbtype}"); + } + else if (memberTbref.Columns[0] == exp3Tb.Primarys[0]) + { + var dbinfo = commonExp._common._orm.CodeFirst.GetDbInfo(memberTbref.RefColumns[0].CsType); + select.Where($"{select._tables[0].Alias}.{commonExp._common.QuoteSqlName(memberTbref.RefColumns[0].Attribute.Name)} @> ARRAY[{amtReftbname}.{commonExp._common.QuoteSqlName(memberTbref.Columns[0].Attribute.Name)}]::{dbinfo?.dbtype}"); + } + else + { + ; + } break; } diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 35fff5825..23250bb11 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -1044,11 +1044,14 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - cscodeExtLogic1 = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; - cscodeExtLogic2 = $" loc2 = this.{trycol.CsName}.Select(a => loc2.FirstOrDefault(b => b.{tbref.Primarys[0].CsName} == a)).ToList();"; - lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName); - if (trycol.CsType.GetElementType().IsNullableType() == false && tbref.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); - lmbdWhere.Append(")"); + if (isLazy) + { + cscodeExtLogic1 = $" if (this.{trycol.CsName} == null) return null; \r\nif (this.{trycol.CsName}.Any() == false) return new {(propTypeIsObservableCollection ? "ObservableCollection" : "List")}<{propElementType.DisplayCsharp()}>();\r\n"; + cscodeExtLogic2 = $" loc2 = this.{trycol.CsName}.Select(a => loc2.FirstOrDefault(b => b.{tbref.Primarys[0].CsName} == a)).ToList();"; + lmbdWhere.Append("this.").Append(trycol.CsName).Append(".Contains(a.").Append(tbref.Primarys[0].CsName); + if (trycol.CsType.GetElementType().IsNullableType() == false && tbref.Primarys[0].CsType.IsNullableType()) lmbdWhere.Append(".Value"); + lmbdWhere.Append(")"); + } nvref.Columns.Add(trycol); nvref.RefColumns.Add(tbref.Primarys[0]); nvref.RefEntityType = tbref.Type; @@ -1091,13 +1094,16 @@ public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo isArrayToMany = trycol != null; if (isArrayToMany) { - lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName); - if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) + if (isLazy) { - lmbdWhere.Append(".Value"); - cscodeExtLogic1 = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + lmbdWhere.Append("a.").Append(trycol.CsName).Append(".Contains(this.").Append(trytb.Primarys[0].CsName); + if (trycol.CsType.GetElementType().IsNullableType() == false && trytb.Primarys[0].CsType.IsNullableType()) + { + lmbdWhere.Append(".Value"); + cscodeExtLogic1 = $" if (this.{trytb.Primarys[0].CsName} == null) return null;\r\n"; + } + lmbdWhere.Append(")"); } - lmbdWhere.Append(")"); nvref.Columns.Add(trytb.Primarys[0]); nvref.RefColumns.Add(trycol); nvref.RefEntityType = tbref.Type;