From 191a15fb421b86b115539d641d63fab4aaf1bbf8 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 20 Apr 2019 01:21:57 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=B4=AA=E5=A9=AA?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=20LeftJoin/InnerJoin/RightJoin=20=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.Tests/Sqlite/SqliteCodeFirstTest.cs | 6 +- FreeSql.Tests/UnitTest1.cs | 4 + .../SelectProvider/Select0Provider.cs | 145 ++++++++++++++++-- 3 files changed, 141 insertions(+), 14 deletions(-) diff --git a/FreeSql.Tests/Sqlite/SqliteCodeFirstTest.cs b/FreeSql.Tests/Sqlite/SqliteCodeFirstTest.cs index e7bb1475..010287ae 100644 --- a/FreeSql.Tests/Sqlite/SqliteCodeFirstTest.cs +++ b/FreeSql.Tests/Sqlite/SqliteCodeFirstTest.cs @@ -10,17 +10,17 @@ namespace FreeSql.Tests.Sqlite { public class SqliteCodeFirstTest { - class Topic { + public class Topic { public Guid Id { get; set; } public string Title { get; set; } public string Content { get; set; } public DateTime CreateTime { get; set; } } [Table(Name = "xxxtb.Comment")] - class Comment { + public class Comment { public Guid Id { get; set; } public Guid TopicId { get; set; } - public Topic Topic { get; set; } + public virtual Topic Topic { get; set; } public string Nickname { get; set; } public string Content { get; set; } public DateTime CreateTime { get; set; } diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs index 3c4b6e96..9502a01f 100644 --- a/FreeSql.Tests/UnitTest1.cs +++ b/FreeSql.Tests/UnitTest1.cs @@ -54,6 +54,10 @@ namespace FreeSql.Tests { [Fact] public void Test1() { + var sql = g.sqlite.Select().Where(a => a.Parent.Parent.Parent.Name == "11").ToSql(); + var sql222 = g.sqlite.Select().Where(a => a.Parent.Parent.Parent.Name == "11").ToList(); + + Expression> orderBy = null; orderBy = a => a.CreateTime; var testsql1 = select.OrderBy(orderBy).ToSql(); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 5305dfa1..5dbe895d 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -46,7 +46,7 @@ namespace FreeSql.Internal.CommonProvider { //toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List(from._tables.ToArray())); var _multiTables = toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(to) as List; _multiTables[0] = from._tables[0]; - for (var a = 1;a < lambParms.Count; a++) { + for (var a = 1; a < lambParms.Count; a++) { var tb = from._tables.Where(b => b.Alias == lambParms[a].Name && b.Table.Type == lambParms[a].Type).FirstOrDefault(); if (tb != null) _multiTables[a] = tb; else { @@ -108,7 +108,7 @@ namespace FreeSql.Internal.CommonProvider { } public long Count() => this.ToList("count(1)").FirstOrDefault(); async public Task CountAsync() => (await this.ToListAsync("count(1)")).FirstOrDefault(); - + public TSelect Count(out long count) { count = this.Count(); return this as TSelect; @@ -235,7 +235,7 @@ namespace FreeSql.Internal.CommonProvider { Type type = typeof(TTuple); _orm.Ado.ExecuteReader(_connection, _transaction, dr => { var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr, 0, _commonUtils); - ret.Add((TTuple)read.Value); + ret.Add((TTuple) read.Value); }, CommandType.Text, sql, _params.ToArray()); _orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret)); _trackToList?.Invoke(ret); @@ -251,7 +251,7 @@ namespace FreeSql.Internal.CommonProvider { Type type = typeof(TTuple); await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => { var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr, 0, _commonUtils); - ret.Add((TTuple)read.Value); + ret.Add((TTuple) read.Value); return Task.CompletedTask; }, CommandType.Text, sql, _params.ToArray()); _orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret)); @@ -311,7 +311,7 @@ namespace FreeSql.Internal.CommonProvider { Type type = typeof(TReturn); _orm.Ado.ExecuteReader(_connection, _transaction, dr => { var index = -1; - ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index, false)); + ret.Add((TReturn) _commonExpression.ReadAnonymous(af.map, dr, ref index, false)); }, CommandType.Text, sql, _params.ToArray()); _orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret)); _trackToList?.Invoke(ret); @@ -327,7 +327,7 @@ namespace FreeSql.Internal.CommonProvider { Type type = typeof(TReturn); await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => { var index = -1; - ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index, false)); + ret.Add((TReturn) _commonExpression.ReadAnonymous(af.map, dr, ref index, false)); return Task.CompletedTask; }, CommandType.Text, sql, _params.ToArray()); _orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret)); @@ -350,6 +350,126 @@ namespace FreeSql.Internal.CommonProvider { public Func Read { get; set; } } protected GetAllFieldExpressionTreeInfo GetAllFieldExpressionTree() { + return _dicGetAllFieldExpressionTree.GetOrAdd(string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Alias}-{a.Type}")), s => { + var type = _tables.First().Table.TypeLazy ?? _tables.First().Table.Type; + var ormExp = Expression.Parameter(typeof(IFreeSql), "orm"); + var rowExp = Expression.Parameter(typeof(DbDataReader), "row"); + var returnTarget = Expression.Label(type); + var retExp = Expression.Variable(type, "ret"); + var dataIndexExp = Expression.Variable(typeof(int), "dataIndex"); + var readExp = Expression.Variable(typeof(Utils.RowInfo), "read"); + var readExpValue = Expression.MakeMemberAccess(readExp, Utils.RowInfo.PropertyValue); + var readExpDataIndex = Expression.MakeMemberAccess(readExp, Utils.RowInfo.PropertyDataIndex); + var blockExp = new List(); + var ctor = type.GetConstructor(new Type[0]) ?? type.GetConstructors().First(); + blockExp.AddRange(new Expression[] { + Expression.Assign(retExp, Expression.New(ctor, ctor.GetParameters().Select(a => Expression.Default(a.ParameterType)))), + Expression.Assign(dataIndexExp, Expression.Constant(0)) + }); + //typeof(Topic).GetMethod("get_Type").IsVirtual + + var field = new StringBuilder(); + var dicfield = new Dictionary(); + var tb = _tables.First(); + var index = 0; + + var tbiindex = 0; + foreach (var tbi in _tables.ToArray().OrderBy(a => a.Alias)) { + if (tbiindex > 0 && tbi.Type == SelectTableInfoType.From) continue; + if (tbiindex > 0 && tbi.Alias.StartsWith($"{tb.Alias}__") == false) continue; + + var typei = tbi.Table.TypeLazy ?? tbi.Table.Type; + if (tbiindex == 0) + blockExp.AddRange(new Expression[] { + Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(typei), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) })), + Expression.IfThen( + Expression.GreaterThan(readExpDataIndex, dataIndexExp), + Expression.Assign(dataIndexExp, readExpDataIndex) + ), + Expression.IfThen( + Expression.NotEqual(readExpValue, Expression.Constant(null)), + Expression.Assign(retExp, Expression.Convert(readExpValue, typei)) + ) + }); + else { + Expression curExpIfNotNull = Expression.IsTrue(Expression.Constant(true)); + Expression curExp = retExp; + var curTb = tb; + var parentNameSplits = tbi.Alias.Split(new[] { "__" }, StringSplitOptions.None); + var iscontinue = false; + for (var k = 1; k < parentNameSplits.Length; k++) { + var curPropName = parentNameSplits[k]; + if (curTb.Table.Properties.TryGetValue(parentNameSplits[k], out var tryprop) == false) { + k++; + curPropName = $"{curPropName}__{parentNameSplits[k]}"; + if (curTb.Table.Properties.TryGetValue(parentNameSplits[k], out tryprop) == false) { + iscontinue = true; + break; + } + } + curExp = Expression.MakeMemberAccess(curExp, tryprop); + if (k + 1 < parentNameSplits.Length) + curExpIfNotNull = Expression.AndAlso(curExpIfNotNull, Expression.NotEqual(curExp, Expression.Default(tryprop.PropertyType))); + curTb = _tables.Where(a => a.Alias == $"{curTb.Alias}__{curPropName}" && a.Table.Type == tryprop.PropertyType).FirstOrDefault(); + if (curTb == null) { + iscontinue = true; + break; + } + } + if (iscontinue) continue; + + blockExp.Add( + Expression.IfThen( + curExpIfNotNull, + Expression.Block(new Expression[] { + Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(typei), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) })), + Expression.IfThen( + Expression.GreaterThan(readExpDataIndex, dataIndexExp), + Expression.Assign(dataIndexExp, readExpDataIndex) + ), + Expression.IfThen( + Expression.NotEqual(readExpValue, Expression.Constant(null)), + Expression.Assign(curExp, Expression.Convert(readExpValue, typei)) + ) + }) + ) + ); + } + if (tbi.Table.TypeLazy != null) + blockExp.Add( + Expression.IfThen( + Expression.NotEqual(readExpValue, Expression.Constant(null)), + Expression.Call(retExp, tbi.Table.TypeLazySetOrm, ormExp) + ) + ); //将 orm 传递给 lazy + + var colidx = 0; + foreach (var col in tbi.Table.Columns.Values) { + if (index > 0) { + field.Append(", "); + if (tbiindex > 0 && colidx == 0) field.Append("\r\n"); + } + var quoteName = _commonUtils.QuoteSqlName(col.Attribute.Name); + field.Append(_commonUtils.QuoteReadColumn(col.CsType, $"{tbi.Alias}.{quoteName}")); + ++index; + if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index); + else dicfield.Add(quoteName, true); + ++colidx; + } + tbiindex++; + } + + blockExp.AddRange(new Expression[] { + Expression.Return(returnTarget, retExp), + Expression.Label(returnTarget, Expression.Default(type)) + }); + return new GetAllFieldExpressionTreeInfo { + Field = field.ToString(), + Read = Expression.Lambda>(Expression.Block(new[] { retExp, dataIndexExp, readExp }, blockExp), new[] { ormExp, rowExp }).Compile() + }; + }); + } + protected GetAllFieldExpressionTreeInfo GetAllFieldExpressionTreeLevel2() { return _dicGetAllFieldExpressionTree.GetOrAdd(string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Alias}-{a.Type}")), s => { var tb1 = _tables.First().Table; var type = tb1.TypeLazy ?? tb1.Type; @@ -387,12 +507,14 @@ namespace FreeSql.Internal.CommonProvider { if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index); else dicfield.Add(quoteName, true); } else { - var tb2 = _tables.Where((a, b) => b > 0 && - (a.Type == SelectTableInfoType.InnerJoin || a.Type == SelectTableInfoType.LeftJoin || a.Type == SelectTableInfoType.RightJoin) && + var tb2 = _tables.Where((a, b) => b > 0 && + (a.Type == SelectTableInfoType.InnerJoin || a.Type == SelectTableInfoType.LeftJoin || a.Type == SelectTableInfoType.RightJoin) && string.IsNullOrEmpty(a.On) == false && - a.Alias.Contains(prop.Name)).FirstOrDefault(); //判断 b > 0 防止 parent 递归关系 + a.Alias.StartsWith($"{tb.Alias}__") && //开头结尾完全匹配 + a.Alias.EndsWith($"__{prop.Name}") //不清楚会不会有其他情况 求大佬优化 + ).FirstOrDefault(); //判断 b > 0 防止 parent 递归关系 if (tb2 == null && props.Where(pw => pw.Value.PropertyType == prop.PropertyType).Count() == 1) - tb2 = _tables.Where((a, b) => b > 0 && + tb2 = _tables.Where((a, b) => b > 0 && (a.Type == SelectTableInfoType.InnerJoin || a.Type == SelectTableInfoType.LeftJoin || a.Type == SelectTableInfoType.RightJoin) && string.IsNullOrEmpty(a.On) == false && a.Table.Type == prop.PropertyType).FirstOrDefault(); @@ -501,7 +623,8 @@ namespace FreeSql.Internal.CommonProvider { else dicfield.Add(quoteName, true); child.Childs.Add(new ReadAnonymousTypeInfo { Property = tb2.Table.Type.GetProperty(col2.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance), - CsName = col2.CsName }); + CsName = col2.CsName + }); } } map.Childs.Add(child); From fd04042bd26b9d84c562174d9fddbc56b8f17e44 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 20 Apr 2019 11:42:29 +0800 Subject: [PATCH 2/2] test --- FreeSql.Tests/FreeSql.Tests.csproj | 2 +- FreeSql.Tests/UnitTest1.cs | 26 ++++++++++++++++++++++++-- FreeSql.sln | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests.csproj b/FreeSql.Tests/FreeSql.Tests.csproj index 78b3314d..2f71b95d 100644 --- a/FreeSql.Tests/FreeSql.Tests.csproj +++ b/FreeSql.Tests/FreeSql.Tests.csproj @@ -7,13 +7,13 @@ - + diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs index 9502a01f..7c737c04 100644 --- a/FreeSql.Tests/UnitTest1.cs +++ b/FreeSql.Tests/UnitTest1.cs @@ -54,8 +54,28 @@ namespace FreeSql.Tests { [Fact] public void Test1() { - var sql = g.sqlite.Select().Where(a => a.Parent.Parent.Parent.Name == "11").ToSql(); - var sql222 = g.sqlite.Select().Where(a => a.Parent.Parent.Parent.Name == "11").ToList(); + var sqlrepos = g.sqlite.GetRepository(); + sqlrepos.Insert(new TestTypeParentInfo { + Name = "testroot", + Childs = new[] { + new TestTypeParentInfo { + Name = "testpath2", + Childs = new[] { + new TestTypeParentInfo { + Name = "testpath3", + Childs = new[] { + new TestTypeParentInfo { + Name = "11" + } + } + } + } + } + } + }); + + var sql = g.sqlite.Select().Where(a => a.Parent.Parent.Parent.Name == "testroot").ToSql(); + var sql222 = g.sqlite.Select().Where(a => a.Parent.Parent.Parent.Name == "testroot").ToList(); Expression> orderBy = null; @@ -336,11 +356,13 @@ namespace FreeSql.Tests { } class TestTypeParentInfo { + [Column(IsIdentity = true)] public int Id { get; set; } public string Name { get; set; } public int ParentId { get; set; } public TestTypeParentInfo Parent { get; set; } + public ICollection Childs { get; set; } public List Types { get; set; } } diff --git a/FreeSql.sln b/FreeSql.sln index 87e56fa9..15541021 100644 --- a/FreeSql.sln +++ b/FreeSql.sln @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "efcore_to_freesql", "Exampl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "orm_vs", "Examples\orm_vs\orm_vs.csproj", "{1A5EC2EB-8C2B-4547-8AC6-EB5C0DE0CA81}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.DbContext", "..\FreeSql.DbContext\FreeSql.DbContext\FreeSql.DbContext.csproj", "{56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -118,6 +120,18 @@ Global {1A5EC2EB-8C2B-4547-8AC6-EB5C0DE0CA81}.Release|x64.Build.0 = Release|Any CPU {1A5EC2EB-8C2B-4547-8AC6-EB5C0DE0CA81}.Release|x86.ActiveCfg = Release|Any CPU {1A5EC2EB-8C2B-4547-8AC6-EB5C0DE0CA81}.Release|x86.Build.0 = Release|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Debug|x64.ActiveCfg = Debug|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Debug|x64.Build.0 = Debug|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Debug|x86.ActiveCfg = Debug|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Debug|x86.Build.0 = Debug|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Release|Any CPU.Build.0 = Release|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Release|x64.ActiveCfg = Release|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Release|x64.Build.0 = Release|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Release|x86.ActiveCfg = Release|Any CPU + {56488C60-FF2C-47ED-B6DD-A38D3CFB84FB}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE