diff --git a/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs b/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs index 78292577..52ed6c06 100644 --- a/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs +++ b/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs @@ -957,6 +957,11 @@ namespace FreeSql.Tests.MySql { }; Assert.Equal(5, g.mysql.Insert(model4s).ExecuteAffrows()); + var t0 = g.mysql.Select() + .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + var t1 = g.mysql.Select() .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id)) .Where(a => a.id <= model1.id) @@ -967,7 +972,76 @@ namespace FreeSql.Tests.MySql { then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id))) .Where(a => a.id <= model1.id) .ToList(); + + var t00 = g.mysql.Select() + .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + + var t11 = g.mysql.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id)) + .Where(a => a.id <= model1.id) + .ToList(); + + var t22 = g.mysql.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id), + then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id))) + .Where(a => a.id <= model1.id) + .ToList(); } + + public class TestInclude_OneToManyModel11 { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2id { get; set; } + public string m3setting { get; set; } + public TestInclude_OneToManyModel22 model2 { get; set; } + public string m1name { get; set; } + } + + public class TestInclude_OneToManyModel22 { + [Column(IsIdentity = true)] + public int id { get; set; } + public string m2setting { get; set; } + public List childs { get; set; } + } + public class TestInclude_OneToManyModel33 { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2Id { get; set; } + public string title { get; set; } + public string setting { get; set; } + } + [Fact] + public void Include_OneToMany2() { + string setting = "x"; + var model2 = new TestInclude_OneToManyModel22 { m2setting = DateTime.Now.Second.ToString() }; + model2.id = (int)g.mysql.Insert(model2).ExecuteIdentity(); + + var model3s = new[] + { + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__111", setting = setting}, + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__222", setting = setting}, + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__333", setting = setting} + }; + Assert.Equal(3, g.mysql.Insert(model3s).ExecuteAffrows()); + + var model1 = new TestInclude_OneToManyModel11 { m1name = DateTime.Now.Second.ToString(), model2id = model2.id, m3setting = setting }; + model1.id = (int)g.mysql.Insert(model1).ExecuteIdentity(); + + var t1 = g.mysql.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + + var t11 = g.mysql.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + } + [Fact] public void Include_OneToChilds() { var tag1 = new Tag { @@ -1011,7 +1085,7 @@ namespace FreeSql.Tests.MySql { .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); - var tags = g.mysql.Select() + var tags1 = g.mysql.Select() .IncludeMany(a => a.Tags) .Include(a => a.Parent) .IncludeMany(a => a.Songs) @@ -1033,6 +1107,29 @@ namespace FreeSql.Tests.MySql { .IncludeMany(a => a.Songs) .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); + + var tags11 = g.mysql.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags22 = g.mysql.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags33 = g.mysql.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1)).IncludeMany(a => a.Tags.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); } [Fact] @@ -1080,14 +1177,14 @@ namespace FreeSql.Tests.MySql { g.mysql.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag2.Id }).ExecuteAffrows(); g.mysql.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag3.Id }).ExecuteAffrows(); - var songs = g.mysql.Select() + var songs1 = g.mysql.Select() .IncludeMany(a => a.Tags) .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) .ToList(); - Assert.Equal(3, songs.Count); - Assert.Equal(2, songs[0].Tags.Count); - Assert.Equal(1, songs[1].Tags.Count); - Assert.Equal(3, songs[2].Tags.Count); + Assert.Equal(3, songs1.Count); + Assert.Equal(2, songs1[0].Tags.Count); + Assert.Equal(1, songs1[1].Tags.Count); + Assert.Equal(3, songs1[2].Tags.Count); var songs2 = g.mysql.Select() .IncludeMany(a => a.Tags, @@ -1098,6 +1195,38 @@ namespace FreeSql.Tests.MySql { Assert.Equal(2, songs2[0].Tags.Count); Assert.Equal(1, songs2[1].Tags.Count); Assert.Equal(3, songs2[2].Tags.Count); + + var tags3 = g.mysql.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); + + + var songs11 = g.mysql.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs11.Count); + Assert.Equal(1, songs11[0].Tags.Count); + Assert.Equal(1, songs11[1].Tags.Count); + Assert.Equal(1, songs11[2].Tags.Count); + + var songs22 = g.mysql.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.IncludeMany(t => t.Songs.Take(1))) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs22.Count); + Assert.Equal(1, songs22[0].Tags.Count); + Assert.Equal(1, songs22[1].Tags.Count); + Assert.Equal(1, songs22[2].Tags.Count); + + var tags33 = g.mysql.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs.Take(1)) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); } } } diff --git a/FreeSql.Tests/Oracle/Curd/OracleSelectTest.cs b/FreeSql.Tests/Oracle/Curd/OracleSelectTest.cs index 3c09e1d3..97f0a718 100644 --- a/FreeSql.Tests/Oracle/Curd/OracleSelectTest.cs +++ b/FreeSql.Tests/Oracle/Curd/OracleSelectTest.cs @@ -850,6 +850,11 @@ namespace FreeSql.Tests.Oracle { }; Assert.Equal(5, g.oracle.Insert(model4s).ExecuteAffrows()); + var t0 = g.oracle.Select() + .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + var t1 = g.oracle.Select() .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id)) .Where(a => a.id <= model1.id) @@ -860,7 +865,76 @@ namespace FreeSql.Tests.Oracle { then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id))) .Where(a => a.id <= model1.id) .ToList(); + + var t00 = g.oracle.Select() + .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + + var t11 = g.oracle.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id)) + .Where(a => a.id <= model1.id) + .ToList(); + + var t22 = g.oracle.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id), + then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id))) + .Where(a => a.id <= model1.id) + .ToList(); } + + public class TiOtmModel11 { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2id { get; set; } + public string m3setting { get; set; } + public TiOtmModel22 model2 { get; set; } + public string m1name { get; set; } + } + + public class TiOtmModel22 { + [Column(IsIdentity = true)] + public int id { get; set; } + public string m2setting { get; set; } + public List childs { get; set; } + } + public class TiOtmModel33 { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2Id { get; set; } + public string title { get; set; } + public string setting { get; set; } + } + [Fact] + public void Include_OneToMany2() { + string setting = "x"; + var model2 = new TiOtmModel22 { m2setting = DateTime.Now.Second.ToString() }; + model2.id = (int)g.oracle.Insert(model2).ExecuteIdentity(); + + var model3s = new[] + { + new TiOtmModel33 {model2Id = model2.id, title = "testmodel3__111", setting = setting}, + new TiOtmModel33 {model2Id = model2.id, title = "testmodel3__222", setting = setting}, + new TiOtmModel33 {model2Id = model2.id, title = "testmodel3__333", setting = setting} + }; + Assert.Equal(3, g.oracle.Insert(model3s).ExecuteAffrows()); + + var model1 = new TiOtmModel11 { m1name = DateTime.Now.Second.ToString(), model2id = model2.id, m3setting = setting }; + model1.id = (int)g.oracle.Insert(model1).ExecuteIdentity(); + + var t1 = g.oracle.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + + var t11 = g.oracle.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + } + [Fact] public void Include_OneToChilds() { var tag1 = new Tag { @@ -904,7 +978,7 @@ namespace FreeSql.Tests.Oracle { .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); - var tags = g.oracle.Select() + var tags1 = g.oracle.Select() .IncludeMany(a => a.Tags) .Include(a => a.Parent) .IncludeMany(a => a.Songs) @@ -926,6 +1000,29 @@ namespace FreeSql.Tests.Oracle { .IncludeMany(a => a.Songs) .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); + + var tags11 = g.oracle.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags22 = g.oracle.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags33 = g.oracle.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1)).IncludeMany(a => a.Tags.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); } [Fact] @@ -973,14 +1070,14 @@ namespace FreeSql.Tests.Oracle { g.oracle.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag2.Id }).ExecuteAffrows(); g.oracle.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag3.Id }).ExecuteAffrows(); - var songs = g.oracle.Select() + var songs1 = g.oracle.Select() .IncludeMany(a => a.Tags) .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) .ToList(); - Assert.Equal(3, songs.Count); - Assert.Equal(2, songs[0].Tags.Count); - Assert.Equal(1, songs[1].Tags.Count); - Assert.Equal(3, songs[2].Tags.Count); + Assert.Equal(3, songs1.Count); + Assert.Equal(2, songs1[0].Tags.Count); + Assert.Equal(1, songs1[1].Tags.Count); + Assert.Equal(3, songs1[2].Tags.Count); var songs2 = g.oracle.Select() .IncludeMany(a => a.Tags, @@ -991,6 +1088,38 @@ namespace FreeSql.Tests.Oracle { Assert.Equal(2, songs2[0].Tags.Count); Assert.Equal(1, songs2[1].Tags.Count); Assert.Equal(3, songs2[2].Tags.Count); + + var tags3 = g.oracle.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); + + + var songs11 = g.oracle.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs11.Count); + Assert.Equal(1, songs11[0].Tags.Count); + Assert.Equal(1, songs11[1].Tags.Count); + Assert.Equal(1, songs11[2].Tags.Count); + + var songs22 = g.oracle.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.IncludeMany(t => t.Songs.Take(1))) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs22.Count); + Assert.Equal(1, songs22[0].Tags.Count); + Assert.Equal(1, songs22[1].Tags.Count); + Assert.Equal(1, songs22[2].Tags.Count); + + var tags33 = g.oracle.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs.Take(1)) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); } } } diff --git a/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLSelectTest.cs b/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLSelectTest.cs index 088b4545..7a5c4064 100644 --- a/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLSelectTest.cs +++ b/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLSelectTest.cs @@ -916,6 +916,11 @@ namespace FreeSql.Tests.PostgreSQL { }; Assert.Equal(5, g.pgsql.Insert(model4s).ExecuteAffrows()); + var t0 = g.pgsql.Select() + .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + var t1 = g.pgsql.Select() .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id)) .Where(a => a.id <= model1.id) @@ -926,7 +931,76 @@ namespace FreeSql.Tests.PostgreSQL { then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id))) .Where(a => a.id <= model1.id) .ToList(); + + var t00 = g.pgsql.Select() + .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + + var t11 = g.pgsql.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id)) + .Where(a => a.id <= model1.id) + .ToList(); + + var t22 = g.pgsql.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id), + then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id))) + .Where(a => a.id <= model1.id) + .ToList(); } + + public class TestInclude_OneToManyModel11 { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2id { get; set; } + public string m3setting { get; set; } + public TestInclude_OneToManyModel22 model2 { get; set; } + public string m1name { get; set; } + } + + public class TestInclude_OneToManyModel22 { + [Column(IsIdentity = true)] + public int id { get; set; } + public string m2setting { get; set; } + public List childs { get; set; } + } + public class TestInclude_OneToManyModel33 { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2Id { get; set; } + public string title { get; set; } + public string setting { get; set; } + } + [Fact] + public void Include_OneToMany2() { + string setting = "x"; + var model2 = new TestInclude_OneToManyModel22 { m2setting = DateTime.Now.Second.ToString() }; + model2.id = (int)g.pgsql.Insert(model2).ExecuteIdentity(); + + var model3s = new[] + { + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__111", setting = setting}, + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__222", setting = setting}, + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__333", setting = setting} + }; + Assert.Equal(3, g.pgsql.Insert(model3s).ExecuteAffrows()); + + var model1 = new TestInclude_OneToManyModel11 { m1name = DateTime.Now.Second.ToString(), model2id = model2.id, m3setting = setting }; + model1.id = (int)g.pgsql.Insert(model1).ExecuteIdentity(); + + var t1 = g.pgsql.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + + var t11 = g.pgsql.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + } + [Fact] public void Include_OneToChilds() { var tag1 = new Tag { @@ -970,7 +1044,7 @@ namespace FreeSql.Tests.PostgreSQL { .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); - var tags = g.pgsql.Select() + var tags1 = g.pgsql.Select() .IncludeMany(a => a.Tags) .Include(a => a.Parent) .IncludeMany(a => a.Songs) @@ -992,6 +1066,29 @@ namespace FreeSql.Tests.PostgreSQL { .IncludeMany(a => a.Songs) .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); + + var tags11 = g.pgsql.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags22 = g.pgsql.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags33 = g.pgsql.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1)).IncludeMany(a => a.Tags.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); } [Fact] @@ -1039,14 +1136,14 @@ namespace FreeSql.Tests.PostgreSQL { g.pgsql.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag2.Id }).ExecuteAffrows(); g.pgsql.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag3.Id }).ExecuteAffrows(); - var songs = g.pgsql.Select() + var songs1 = g.pgsql.Select() .IncludeMany(a => a.Tags) .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) .ToList(); - Assert.Equal(3, songs.Count); - Assert.Equal(2, songs[0].Tags.Count); - Assert.Equal(1, songs[1].Tags.Count); - Assert.Equal(3, songs[2].Tags.Count); + Assert.Equal(3, songs1.Count); + Assert.Equal(2, songs1[0].Tags.Count); + Assert.Equal(1, songs1[1].Tags.Count); + Assert.Equal(3, songs1[2].Tags.Count); var songs2 = g.pgsql.Select() .IncludeMany(a => a.Tags, @@ -1057,6 +1154,38 @@ namespace FreeSql.Tests.PostgreSQL { Assert.Equal(2, songs2[0].Tags.Count); Assert.Equal(1, songs2[1].Tags.Count); Assert.Equal(3, songs2[2].Tags.Count); + + var tags3 = g.pgsql.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); + + + var songs11 = g.pgsql.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs11.Count); + Assert.Equal(1, songs11[0].Tags.Count); + Assert.Equal(1, songs11[1].Tags.Count); + Assert.Equal(1, songs11[2].Tags.Count); + + var songs22 = g.pgsql.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.IncludeMany(t => t.Songs.Take(1))) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs22.Count); + Assert.Equal(1, songs22[0].Tags.Count); + Assert.Equal(1, songs22[1].Tags.Count); + Assert.Equal(1, songs22[2].Tags.Count); + + var tags33 = g.pgsql.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs.Take(1)) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); } } } diff --git a/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs b/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs index 3e37bf8b..56c58afc 100644 --- a/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs +++ b/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs @@ -847,6 +847,11 @@ namespace FreeSql.Tests.SqlServer { }; Assert.Equal(5, _sqlserverFixture.SqlServer.Insert(model4s).ExecuteAffrows()); + var t0 = _sqlserverFixture.SqlServer.Select() + .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + var t1 = _sqlserverFixture.SqlServer.Select() .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id)) .Where(a => a.id <= model1.id) @@ -857,7 +862,76 @@ namespace FreeSql.Tests.SqlServer { then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id))) .Where(a => a.id <= model1.id) .ToList(); + + var t00 = _sqlserverFixture.SqlServer.Select() + .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + + var t11 = _sqlserverFixture.SqlServer.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id)) + .Where(a => a.id <= model1.id) + .ToList(); + + var t22 = _sqlserverFixture.SqlServer.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id), + then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id))) + .Where(a => a.id <= model1.id) + .ToList(); } + + public class TestInclude_OneToManyModel11 { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2id { get; set; } + public string m3setting { get; set; } + public TestInclude_OneToManyModel22 model2 { get; set; } + public string m1name { get; set; } + } + + public class TestInclude_OneToManyModel22 { + [Column(IsIdentity = true)] + public int id { get; set; } + public string m2setting { get; set; } + public List childs { get; set; } + } + public class TestInclude_OneToManyModel33 { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2Id { get; set; } + public string title { get; set; } + public string setting { get; set; } + } + [Fact] + public void Include_OneToMany2() { + string setting = "x"; + var model2 = new TestInclude_OneToManyModel22 { m2setting = DateTime.Now.Second.ToString() }; + model2.id = (int)_sqlserverFixture.SqlServer.Insert(model2).ExecuteIdentity(); + + var model3s = new[] + { + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__111", setting = setting}, + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__222", setting = setting}, + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__333", setting = setting} + }; + Assert.Equal(3, _sqlserverFixture.SqlServer.Insert(model3s).ExecuteAffrows()); + + var model1 = new TestInclude_OneToManyModel11 { m1name = DateTime.Now.Second.ToString(), model2id = model2.id, m3setting = setting }; + model1.id = (int)_sqlserverFixture.SqlServer.Insert(model1).ExecuteIdentity(); + + var t1 = _sqlserverFixture.SqlServer.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + + var t11 = _sqlserverFixture.SqlServer.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + } + [Fact] public void Include_OneToChilds() { var tag1 = new Tag { @@ -901,7 +975,7 @@ namespace FreeSql.Tests.SqlServer { .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); - var tags = _sqlserverFixture.SqlServer.Select() + var tags1 = _sqlserverFixture.SqlServer.Select() .IncludeMany(a => a.Tags) .Include(a => a.Parent) .IncludeMany(a => a.Songs) @@ -923,6 +997,29 @@ namespace FreeSql.Tests.SqlServer { .IncludeMany(a => a.Songs) .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); + + var tags11 = _sqlserverFixture.SqlServer.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags22 = _sqlserverFixture.SqlServer.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags33 = _sqlserverFixture.SqlServer.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1)).IncludeMany(a => a.Tags.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); } [Fact] @@ -970,14 +1067,14 @@ namespace FreeSql.Tests.SqlServer { _sqlserverFixture.SqlServer.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag2.Id }).ExecuteAffrows(); _sqlserverFixture.SqlServer.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag3.Id }).ExecuteAffrows(); - var songs = _sqlserverFixture.SqlServer.Select() + var songs1 = _sqlserverFixture.SqlServer.Select() .IncludeMany(a => a.Tags) .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) .ToList(); - Assert.Equal(3, songs.Count); - Assert.Equal(2, songs[0].Tags.Count); - Assert.Equal(1, songs[1].Tags.Count); - Assert.Equal(3, songs[2].Tags.Count); + Assert.Equal(3, songs1.Count); + Assert.Equal(2, songs1[0].Tags.Count); + Assert.Equal(1, songs1[1].Tags.Count); + Assert.Equal(3, songs1[2].Tags.Count); var songs2 = _sqlserverFixture.SqlServer.Select() .IncludeMany(a => a.Tags, @@ -988,6 +1085,38 @@ namespace FreeSql.Tests.SqlServer { Assert.Equal(2, songs2[0].Tags.Count); Assert.Equal(1, songs2[1].Tags.Count); Assert.Equal(3, songs2[2].Tags.Count); + + var tags3 = _sqlserverFixture.SqlServer.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); + + + var songs11 = _sqlserverFixture.SqlServer.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs11.Count); + Assert.Equal(1, songs11[0].Tags.Count); + Assert.Equal(1, songs11[1].Tags.Count); + Assert.Equal(1, songs11[2].Tags.Count); + + var songs22 = _sqlserverFixture.SqlServer.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.IncludeMany(t => t.Songs.Take(1))) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs22.Count); + Assert.Equal(1, songs22[0].Tags.Count); + Assert.Equal(1, songs22[1].Tags.Count); + Assert.Equal(1, songs22[2].Tags.Count); + + var tags33 = _sqlserverFixture.SqlServer.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs.Take(1)) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); } } } diff --git a/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs b/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs index dea4d456..e4a45982 100644 --- a/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs +++ b/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs @@ -828,6 +828,22 @@ namespace FreeSql.Tests.Sqlite { then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id))) .Where(a => a.id <= model1.id) .ToList(); + + var t00 = g.sqlite.Select() + .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + + var t11 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id)) + .Where(a => a.id <= model1.id) + .ToList(); + + var t22 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id), + then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id))) + .Where(a => a.id <= model1.id) + .ToList(); } public class TestInclude_OneToManyModel11 { @@ -872,7 +888,13 @@ namespace FreeSql.Tests.Sqlite { var t1 = g.sqlite.Select() .LeftJoin(a => a.model2id == a.model2.id) .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) - .Where(a => a.id == model1.id) + .Where(a => a.id <= model1.id) + .ToList(true); + + var t11 = g.sqlite.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) .ToList(true); } @@ -919,7 +941,7 @@ namespace FreeSql.Tests.Sqlite { .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); - var tags = g.sqlite.Select() + var tags1 = g.sqlite.Select() .IncludeMany(a => a.Tags) .Include(a => a.Parent) .IncludeMany(a => a.Songs) @@ -941,6 +963,29 @@ namespace FreeSql.Tests.Sqlite { .IncludeMany(a => a.Songs) .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); + + var tags11 = g.sqlite.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags22 = g.sqlite.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags33 = g.sqlite.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1)).IncludeMany(a => a.Tags.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); } [Fact] @@ -988,14 +1033,14 @@ namespace FreeSql.Tests.Sqlite { g.sqlite.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag2.Id }).ExecuteAffrows(); g.sqlite.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag3.Id }).ExecuteAffrows(); - var songs = g.sqlite.Select() + var songs1 = g.sqlite.Select() .IncludeMany(a => a.Tags) .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) .ToList(); - Assert.Equal(3, songs.Count); - Assert.Equal(2, songs[0].Tags.Count); - Assert.Equal(1, songs[1].Tags.Count); - Assert.Equal(3, songs[2].Tags.Count); + Assert.Equal(3, songs1.Count); + Assert.Equal(2, songs1[0].Tags.Count); + Assert.Equal(1, songs1[1].Tags.Count); + Assert.Equal(3, songs1[2].Tags.Count); var songs2 = g.sqlite.Select() .IncludeMany(a => a.Tags, @@ -1012,6 +1057,32 @@ namespace FreeSql.Tests.Sqlite { .IncludeMany(a => a.Tag.Songs) .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) .ToList(true); + + + var songs11 = g.sqlite.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs11.Count); + Assert.Equal(1, songs11[0].Tags.Count); + Assert.Equal(1, songs11[1].Tags.Count); + Assert.Equal(1, songs11[2].Tags.Count); + + var songs22 = g.sqlite.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.IncludeMany(t => t.Songs.Take(1))) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs22.Count); + Assert.Equal(1, songs22[0].Tags.Count); + Assert.Equal(1, songs22[1].Tags.Count); + Assert.Equal(1, songs22[2].Tags.Count); + + var tags33 = g.sqlite.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs.Take(1)) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); } } } diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs index 75085107..e2f1c5f5 100644 --- a/FreeSql.Tests/UnitTest1.cs +++ b/FreeSql.Tests/UnitTest1.cs @@ -135,8 +135,8 @@ namespace FreeSql.Tests { }); var includet1 = g.sqlite.Select() - .IncludeMany(a => a.Childs, s => s.Where(a => a.id > 0)) - .IncludeMany(a => a.TestManys.Where(b => b.id == a.id)) + .IncludeMany(a => a.Childs.Take(2), s => s.Where(a => a.id > 0)) + .IncludeMany(a => a.TestManys.Take(1).Where(b => b.id == a.id)) .Where(a => a.id > 10) .ToList(); diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index fbd4d362..421a2a47 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.5.20 + 0.5.21 true YeXiangQin FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite. diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index ae999852..7c722880 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1276,7 +1276,7 @@ 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装 - 选择一个集合的导航属性,也可通过 .Where 设置临时的关系映射 + 选择一个集合的导航属性,也可通过 .Where 设置临时的关系映射,还可以 .Take(5) 每个子集合只取5条 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) diff --git a/FreeSql/Interface/Curd/ISelect/ISelect1.cs b/FreeSql/Interface/Curd/ISelect/ISelect1.cs index d3881e32..3abdf2ed 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect1.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect1.cs @@ -305,7 +305,7 @@ namespace FreeSql { /// 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装 /// /// - /// 选择一个集合的导航属性,也可通过 .Where 设置临时的关系映射 + /// 选择一个集合的导航属性,也可通过 .Where 设置临时的关系映射,还可以 .Take(5) 每个子集合只取5条 /// 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) /// ISelect IncludeMany(Expression>> navigateSelector, Action> then = null) where TNavigate : class; diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index d5e6cd7d..4b029f4f 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -318,15 +318,7 @@ namespace FreeSql.Internal.CommonProvider { return ret; }); } - internal List ToListPrivate(GetAllFieldExpressionTreeInfo af, (string field, ReadAnonymousTypeInfo read, List retlist)[] otherData) { - string sql = null; - if (otherData?.Length > 0) { - var sbField = new StringBuilder().Append(af.Field); - foreach (var other in otherData) - sbField.Append(other.field); - sql = this.ToSql(sbField.ToString()); - } else - sql = this.ToSql(af.Field); + internal List ToListAfPrivate(string sql, GetAllFieldExpressionTreeInfo af, (string field, ReadAnonymousTypeInfo read, List retlist)[] otherData) { if (_cache.seconds > 0 && string.IsNullOrEmpty(_cache.key)) _cache.key = $"{sql}{string.Join("|", _params.Select(a => a.Value))}"; return _orm.Cache.Shell(_cache.key, _cache.seconds, () => { @@ -357,15 +349,7 @@ namespace FreeSql.Internal.CommonProvider { return ret; }); } - async internal Task> ToListPrivateAsync(GetAllFieldExpressionTreeInfo af, (string field, ReadAnonymousTypeInfo read, List retlist)[] otherData) { - string sql = null; - if (otherData?.Length > 0) { - var sbField = new StringBuilder().Append(af.Field); - foreach (var other in otherData) - sbField.Append(other.field); - sql = this.ToSql(sbField.ToString()); - } else - sql = this.ToSql(af.Field); + async internal Task> ToListAfPrivateAsync(string sql, GetAllFieldExpressionTreeInfo af, (string field, ReadAnonymousTypeInfo read, List retlist)[] otherData) { if (_cache.seconds > 0 && string.IsNullOrEmpty(_cache.key)) _cache.key = $"{sql}{string.Join("|", _params.Select(a => a.Value))}"; return await _orm.Cache.ShellAsync(_cache.key, _cache.seconds, async () => { @@ -397,6 +381,30 @@ namespace FreeSql.Internal.CommonProvider { return ret; }); } + internal List ToListPrivate(GetAllFieldExpressionTreeInfo af, (string field, ReadAnonymousTypeInfo read, List retlist)[] otherData) { + string sql = null; + if (otherData?.Length > 0) { + var sbField = new StringBuilder().Append(af.Field); + foreach (var other in otherData) + sbField.Append(other.field); + sql = this.ToSql(sbField.ToString()); + } else + sql = this.ToSql(af.Field); + + return ToListAfPrivate(sql, af, otherData); + } + internal Task> ToListPrivateAsync(GetAllFieldExpressionTreeInfo af, (string field, ReadAnonymousTypeInfo read, List retlist)[] otherData) { + string sql = null; + if (otherData?.Length > 0) { + var sbField = new StringBuilder().Append(af.Field); + foreach (var other in otherData) + sbField.Append(other.field); + sql = this.ToSql(sbField.ToString()); + } else + sql = this.ToSql(af.Field); + + return ToListAfPrivateAsync(sql, af, otherData); + } public List ToList(bool includeNestedMembers = false) { if (_selectExpression != null) return this.InternalToList(_selectExpression); return this.ToListPrivate(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 4e9f9017..bddf725c 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -167,7 +167,7 @@ namespace FreeSql.Internal.CommonProvider { _tables[0].Parameter = resultSelector.Parameters[0]; _commonExpression.ExpressionLambdaToSql(outerKeySelector, new CommonExpression.ExpTSC { _tables = _tables }); this.InternalJoin(Expression.Lambda>( - Expression.Equal(outerKeySelector.Body, innerKeySelector.Body), + Expression.Equal(outerKeySelector.Body, innerKeySelector.Body), new[] { outerKeySelector.Parameters[0], innerKeySelector.Parameters[0] } ), SelectTableInfoType.InnerJoin); if (typeof(TResult) == typeof(T1)) return this as ISelect; @@ -346,11 +346,18 @@ namespace FreeSql.Internal.CommonProvider { var expBody = navigateSelector?.Body; if (expBody == null) return this; MethodCallExpression whereExp = null; - if (expBody.NodeType == ExpressionType.Call) { - throwNavigateSelector = new Exception($"IncludeMany {nameof(navigateSelector)} 参数类型错误,表达式格式应该是 a.collection.Where(c => c.aid == a.id)"); - whereExp = (expBody as MethodCallExpression); - if (whereExp.Method.Name != "Where") throw throwNavigateSelector; - expBody = whereExp.Object ?? whereExp.Arguments.FirstOrDefault(); + int takeNumber = 0; + while (expBody.NodeType == ExpressionType.Call) { + throwNavigateSelector = new Exception($"IncludeMany {nameof(navigateSelector)} 参数类型错误,表达式格式应该是 a.collections.Take(1).Where(c => c.aid == a.id)"); + var callExp = (expBody as MethodCallExpression); + switch (callExp.Method.Name) { + case "Where": + whereExp = callExp; + break; + case "Take": takeNumber = int.Parse((callExp.Arguments[1] as ConstantExpression)?.Value?.ToString() ?? "0"); break; + default: throw throwNavigateSelector; + } + expBody = callExp.Object ?? callExp.Arguments.FirstOrDefault(); } if (expBody.NodeType != ExpressionType.MemberAccess) throw throwNavigateSelector; @@ -461,7 +468,7 @@ namespace FreeSql.Internal.CommonProvider { var t1parm = Expression.Parameter(typeof(T1)); Expression membersExp = t1parm; - foreach(var mem in members) membersExp = Expression.MakeMemberAccess(membersExp, mem.Member); + foreach (var mem in members) membersExp = Expression.MakeMemberAccess(membersExp, mem.Member); members.Clear(); var listValueExp = Expression.Parameter(typeof(List), "listValue"); @@ -500,31 +507,24 @@ namespace FreeSql.Internal.CommonProvider { return getListValue1(item, propName); }; - foreach (var item in list) { + foreach (var item in list) setListValue(item, null); - } - var subSelect = _orm.Select().WithConnection(_connection).WithTransaction(_transaction).TrackToList(_trackToList); + + var subSelect = _orm.Select().WithConnection(_connection).WithTransaction(_transaction).TrackToList(_trackToList) as Select1Provider; if (_tableRules?.Any() == true) foreach (var tr in _tableRules) subSelect.AsTable(tr); + then?.Invoke(subSelect); + var subSelectT1Alias = subSelect._tables[0].Alias; + var oldWhere = subSelect._where.ToString(); + if (oldWhere.StartsWith(" AND ")) oldWhere = oldWhere.Remove(0, 5); switch (tbref.RefType) { case TableRefType.OneToMany: if (true) { + var subList = new List(); var tbref2 = _commonUtils.GetTableByEntity(tbref.RefEntityType); - if (tbref.Columns.Count == 1) { - var arrExp = Expression.NewArrayInit(tbref.Columns[0].CsType, - list.Select(a => getListValue(a, tbref.Columns[0].CsName, 0)).Distinct() - .Select(a => Expression.Constant(Convert.ChangeType(a, tbref.Columns[0].CsType))).ToArray()); - var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); - var containsMethod = _dicTypeMethod.GetOrAdd(tbref.Columns[0].CsType, et => new ConcurrentDictionary()).GetOrAdd("Contains", mn => - typeof(Enumerable).GetMethods().Where(a => a.Name == mn).First()).MakeGenericMethod(tbref.Columns[0].CsType); - var refCol = Expression.MakeMemberAccess(otmExpParm1, tbref2.Properties[tbref.RefColumns[0].CsName]); - if (refCol.Type.IsNullableType()) refCol = Expression.Property(refCol, CommonExpression._dicNullableValueProperty.GetOrAdd(refCol.Type, ct1 => ct1.GetProperty("Value"))); - subSelect.Where(Expression.Lambda>( - Expression.Call(null, containsMethod, arrExp, refCol), otmExpParm1)); - } else { - var subSelectT1Alias = (subSelect as Select1Provider)._tables[0].Alias; - Dictionary sbDic = new Dictionary(); + Func> getWhereDic = () => { + var sbDic = new Dictionary(); for (var y = 0; y < list.Count; y++) { var sbWhereOne = new StringBuilder(); sbWhereOne.Append("("); @@ -537,28 +537,48 @@ namespace FreeSql.Internal.CommonProvider { sbWhereOne.Clear(); if (sbDic.ContainsKey(whereOne) == false) sbDic.Add(whereOne, true); } - var sbWhere = new StringBuilder(); - foreach (var sbd in sbDic) - sbWhere.Append(" OR ").Append(sbd.Key); - subSelect.Where(sbWhere.Remove(0, 4).ToString()); - sbWhere.Clear(); - //var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); - //Expression expOr = null; - //foreach (var item in list) { - // Expression expAnd = null; - // for (var z = 0; z < tbref.Columns.Count; z++) { - // var colVal = getListValue(item, tbref.Columns[z].CsName); - // var expTmp = Expression.Equal(Expression.MakeMemberAccess(otmExpParm1, tbref2.Properties[tbref.RefColumns[z].CsName]), Expression.Constant(colVal)); - // if (z == 0) expAnd = expTmp; - // else expAnd = Expression.AndAlso(expAnd, expTmp); - // } - // if (expOr == null) expOr = expAnd; - // else expOr = Expression.OrElse(expOr, expAnd); - //} - //subSelect.Where(Expression.Lambda>(expOr, otmExpParm1)); + return sbDic; + }; + if (takeNumber > 0) { + var af = subSelect.GetAllFieldExpressionTreeLevelAll(); + var sbSql = new StringBuilder(); + var sbDic = getWhereDic(); + foreach (var sbd in sbDic) { + subSelect._where.Clear(); + subSelect.Where(sbd.Key).Where(oldWhere).Limit(takeNumber); + sbSql.Append("\r\nUNION ALL\r\nselect * from (").Append(subSelect.ToSql(af.Field)).Append(") ftb"); + } + sbSql.Remove(0, 13); + if (sbDic.Count == 1) sbSql.Remove(0, 15).Remove(sbSql.Length - 5, 5); + sbDic.Clear(); + subList = subSelect.ToListAfPrivate(sbSql.ToString(), af, null); + sbSql.Clear(); + } else { + subSelect._where.Clear(); + if (tbref.Columns.Count == 1) { + var arrExp = Expression.NewArrayInit(tbref.Columns[0].CsType, + list.Select(a => getListValue(a, tbref.Columns[0].CsName, 0)).Distinct() + .Select(a => Expression.Constant(Convert.ChangeType(a, tbref.Columns[0].CsType))).ToArray()); + var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); + var containsMethod = _dicTypeMethod.GetOrAdd(tbref.Columns[0].CsType, et => new ConcurrentDictionary()).GetOrAdd("Contains", mn => + typeof(Enumerable).GetMethods().Where(a => a.Name == mn).First()).MakeGenericMethod(tbref.Columns[0].CsType); + var refCol = Expression.MakeMemberAccess(otmExpParm1, tbref2.Properties[tbref.RefColumns[0].CsName]); + if (refCol.Type.IsNullableType()) refCol = Expression.Property(refCol, CommonExpression._dicNullableValueProperty.GetOrAdd(refCol.Type, ct1 => ct1.GetProperty("Value"))); + subSelect.Where(Expression.Lambda>( + Expression.Call(null, containsMethod, arrExp, refCol), otmExpParm1)); + } else { + var sbDic = getWhereDic(); + var sbWhere = new StringBuilder(); + foreach (var sbd in sbDic) + sbWhere.Append(" OR ").Append(sbd.Key); + subSelect.Where(sbWhere.Remove(0, 4).ToString()); + sbWhere.Clear(); + sbDic.Clear(); + } + subSelect.Where(oldWhere); + subList = subSelect.ToList(true); } - then?.Invoke(subSelect); - var subList = subSelect.ToList(true); + if (subList.Any() == false) { foreach (var item in list) setListValue(item, new List()); @@ -616,6 +636,8 @@ namespace FreeSql.Internal.CommonProvider { break; case TableRefType.ManyToMany: if (true) { + List subList = null; + List midList = new List(); var tbref2 = _commonUtils.GetTableByEntity(tbref.RefEntityType); var tbrefMid = _commonUtils.GetTableByEntity(tbref.RefMiddleEntityType); var sbJoin = new StringBuilder().Append($"{_commonUtils.QuoteSqlName(tbrefMid.DbName)} midtb ON "); @@ -625,35 +647,11 @@ namespace FreeSql.Internal.CommonProvider { } subSelect.InnerJoin(sbJoin.ToString()); sbJoin.Clear(); - if (tbref.Columns.Count == 1) { - subSelect.Where(_commonUtils.FormatSql($"midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[0].Attribute.Name)} in {{0}}", list.Select(a => getListValue1(a, tbref.Columns[0].CsName)).Distinct())); - } else { - Dictionary sbDic = new Dictionary(); - for (var y = 0; y < list.Count; y++) { - var sbWhereOne = new StringBuilder(); - sbWhereOne.Append("("); - for (var z = 0; z < tbref.Columns.Count; z++) { - if (z > 0) sbWhereOne.Append(" AND "); - sbWhereOne.Append(_commonUtils.FormatSql($" midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[z].Attribute.Name)}={{0}}", getListValue1(list[y], tbref.Columns[z].CsName))); - } - sbWhereOne.Append(")"); - var whereOne = sbWhereOne.ToString(); - sbWhereOne.Clear(); - if (sbDic.ContainsKey(whereOne) == false) sbDic.Add(whereOne, true); - } - var sbWhere = new StringBuilder(); - foreach (var sbd in sbDic) - sbWhere.Append(" OR ").Append(sbd.Key); - subSelect.Where(sbWhere.Remove(0, 4).ToString()); - sbWhere.Clear(); - } - then?.Invoke(subSelect); - List subList = null; - List midList = new List(); + var af = subSelect.GetAllFieldExpressionTreeLevelAll(); + (string field, ReadAnonymousTypeInfo read)? otherData = null; + var sbSql = new StringBuilder(); - var subSelectP1 = (subSelect as Select1Provider); - var af = subSelectP1.GetAllFieldExpressionTreeLevelAll(); if (_selectExpression == null) {// return this.InternalToList(_selectExpression).Select(a => (a, ()).ToList(); var field = new StringBuilder(); var read = new ReadAnonymousTypeInfo(); @@ -672,10 +670,53 @@ namespace FreeSql.Internal.CommonProvider { read.Childs.Add(child); field.Append(", ").Append(_commonUtils.QuoteReadColumn(child.MapType, child.DbField)); } - subList = subSelectP1.ToListPrivate(af, new[] { (field.ToString(), read, midList) }); - } else - subList = subSelectP1.ToListPrivate(af, null); + otherData = (field.ToString(), read); + } + Func> getWhereDic = () => { + var sbDic = new Dictionary(); + for (var y = 0; y < list.Count; y++) { + var sbWhereOne = new StringBuilder(); + sbWhereOne.Append("("); + for (var z = 0; z < tbref.Columns.Count; z++) { + if (z > 0) sbWhereOne.Append(" AND "); + sbWhereOne.Append(_commonUtils.FormatSql($" midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[z].Attribute.Name)}={{0}}", getListValue1(list[y], tbref.Columns[z].CsName))); + } + sbWhereOne.Append(")"); + var whereOne = sbWhereOne.ToString(); + sbWhereOne.Clear(); + if (sbDic.ContainsKey(whereOne) == false) sbDic.Add(whereOne, true); + } + return sbDic; + }; + if (takeNumber > 0) { + var sbDic = getWhereDic(); + foreach (var sbd in sbDic) { + subSelect._where.Clear(); + subSelect.Where(sbd.Key).Where(oldWhere).Limit(takeNumber); + sbSql.Append("\r\nUNION ALL\r\nselect * from (").Append(subSelect.ToSql($"{af.Field}{otherData?.field}")).Append(") ftb"); + } + sbSql.Remove(0, 13); + if (sbDic.Count == 1) sbSql.Remove(0, 15).Remove(sbSql.Length - 5, 5); + sbDic.Clear(); + } else { + subSelect._where.Clear(); + if (tbref.Columns.Count == 1) { + subSelect.Where(_commonUtils.FormatSql($"midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[0].Attribute.Name)} in {{0}}", list.Select(a => getListValue1(a, tbref.Columns[0].CsName)).Distinct())); + } else { + var sbDic = getWhereDic(); + var sbWhere = new StringBuilder(); + foreach (var sbd in sbDic) + sbWhere.Append(" OR ").Append(sbd.Key); + subSelect.Where(sbWhere.Remove(0, 4).ToString()); + sbWhere.Clear(); + sbDic.Clear(); + } + subSelect.Where(oldWhere); + sbSql.Append(subSelect.ToSql($"{af.Field}{otherData?.field}")); + } + + subList = subSelect.ToListAfPrivate(sbSql.ToString(), af, otherData == null ? null : new[] { (otherData.Value.field, otherData.Value.read, midList) }); if (subList.Any() == false) { foreach (var item in list) setListValue(item, new List());