From 59b1b7220d835cd52962dad765085a7825b9ebcd Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Tue, 7 May 2019 19:09:19 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20linq=20to=20sql=20?= =?UTF-8?q?=E7=9A=84=E6=9F=A5=E8=AF=A2=E8=AF=AD=E6=B3=95=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LinqToSql/SqliteLinqToSqlTests.cs | 187 ++++++++++++++++++ FreeSql.Tests/UnitTest1.cs | 12 ++ FreeSql/Extensions/FreeSqlGlobalExtensions.cs | 1 + FreeSql/FreeSql.csproj | 2 +- FreeSql/FreeSql.xml | 30 +++ FreeSql/Interface/Curd/ISelect/ISelect1.cs | 21 ++ .../Interface/Curd/ISelect/ISelectGrouping.cs | 5 + FreeSql/Internal/CommonExpression.cs | 2 +- .../SelectProvider/Select0Provider.cs | 39 ++-- .../SelectProvider/Select1Provider.cs | 65 ++++++ .../SelectProvider/SelectGroupingProvider.cs | 1 + FreeSql/Internal/UtilsExpressionTree.cs | 4 +- FreeSql/Sqlite/SqliteCodeFirst.cs | 2 +- 13 files changed, 351 insertions(+), 20 deletions(-) create mode 100644 FreeSql.Tests/LinqToSql/SqliteLinqToSqlTests.cs diff --git a/FreeSql.Tests/LinqToSql/SqliteLinqToSqlTests.cs b/FreeSql.Tests/LinqToSql/SqliteLinqToSqlTests.cs new file mode 100644 index 00000000..40efaa79 --- /dev/null +++ b/FreeSql.Tests/LinqToSql/SqliteLinqToSqlTests.cs @@ -0,0 +1,187 @@ +using FreeSql.DataAnnotations; +using System; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.LinqToSql { + + + class TestLinqToSql { + public Guid id { get; set; } + + public string name { get; set; } + + public int click { get; set; } = 10; + + public DateTime createtime { get; set; } = DateTime.Now; + } + class TestLinqToSqlComment { + public Guid id { get; set; } + + public Guid TestLinqToSqlId { get; set; } + public TestLinqToSql TEstLinqToSql { get; set; } + + public string text { get; set; } + + public DateTime createtime { get; set; } = DateTime.Now; + } + + public class SqliteLinqToSqlTests { + + [Fact] + public void Where() { + var item = new TestLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select() + where a.id == item.id + select a).ToList(); + Assert.True(t1.Any()); + Assert.Equal(item.id, t1[0].id); + } + + [Fact] + public void Select() { + var item = new TestLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select() + where a.id == item.id + select new { a.id }).ToList(); + Assert.True(t1.Any()); + Assert.Equal(item.id, t1[0].id); + } + + [Fact] + public void GroupBy() { + var item = new TestLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select() + where a.id == item.id + group a by new {a.id, a.name } into g + select new { + g.Key.id, g.Key.name, + cou = g.Count(), + avg = g.Avg(g.Value.click), + sum = g.Sum(g.Value.click), + max = g.Max(g.Value.click), + min = g.Min(g.Value.click) + }).ToList(); + Assert.True(t1.Any()); + Assert.Equal(item.id, t1.First().id); + } + + [Fact] + public void CaseWhen() { + var item = new TestLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select() + where a.id == item.id + select new { + a.id, + a.name, + testsub = new { + time = a.click > 10 ? "大于" : "小于或等于" + } + }).ToList(); + Assert.True(t1.Any()); + Assert.Equal(item.id, t1[0].id); + Assert.Equal("小于或等于", t1[0].testsub.time); + } + + [Fact] + public void Join() { + var item = new TestLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + var comment = new TestLinqToSqlComment { TestLinqToSqlId = item.id, text = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(comment).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select() + join b in g.sqlite.Select() on a.id equals b.TestLinqToSqlId + select a).ToList(); + Assert.True(t1.Any()); + //Assert.Equal(item.id, t1[0].id); + + var t2 = (from a in g.sqlite.Select() + join b in g.sqlite.Select() on a.id equals b.TestLinqToSqlId + select new { a.id, bid = b.id }).ToList(); + Assert.True(t2.Any()); + //Assert.Equal(item.id, t2[0].id); + //Assert.Equal(comment.id, t2[0].bid); + + var t3 = (from a in g.sqlite.Select() + join b in g.sqlite.Select() on a.id equals b.TestLinqToSqlId + where a.id == item.id + select new { a.id, bid = b.id }).ToList(); + Assert.True(t3.Any()); + Assert.Equal(item.id, t3[0].id); + Assert.Equal(comment.id, t3[0].bid); + } + + [Fact] + public void LeftJoin() { + var item = new TestLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + var comment = new TestLinqToSqlComment { TestLinqToSqlId = item.id, text = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(comment).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select() + join b in g.sqlite.Select() on a.id equals b.TestLinqToSqlId into temp + from tc in temp.DefaultIfEmpty() + select a).ToList(); + Assert.True(t1.Any()); + //Assert.Equal(item.id, t1[0].id); + + var t2 = (from a in g.sqlite.Select() + join b in g.sqlite.Select() on a.id equals b.TestLinqToSqlId into temp + from tc in temp.DefaultIfEmpty() + select new { a.id, bid = tc.id }).ToList(); + Assert.True(t2.Any()); + //Assert.Equal(item.id, t2[0].id); + //Assert.Equal(comment.id, t2[0].bid); + + var t3 = (from a in g.sqlite.Select() + join b in g.sqlite.Select() on a.id equals b.TestLinqToSqlId into temp + from tc in temp.DefaultIfEmpty() + where a.id == item.id + select new { a.id, bid = tc.id }).ToList(); + Assert.True(t3.Any()); + Assert.Equal(item.id, t3[0].id); + Assert.Equal(comment.id, t3[0].bid); + } + + [Fact] + public void From() { + var item = new TestLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + var comment = new TestLinqToSqlComment { TestLinqToSqlId = item.id, text = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(comment).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select() + from b in g.sqlite.Select() + where a.id == b.TestLinqToSqlId + select a).ToList(); + Assert.True(t1.Any()); + //Assert.Equal(item.id, t1[0].id); + + var t2 = (from a in g.sqlite.Select() + from b in g.sqlite.Select() + where a.id == b.TestLinqToSqlId + select new { a.id, bid = b.id }).ToList(); + Assert.True(t2.Any()); + //Assert.Equal(item.id, t2[0].id); + //Assert.Equal(comment.id, t2[0].bid); + + var t3 = (from a in g.sqlite.Select() + from b in g.sqlite.Select() + where a.id == b.TestLinqToSqlId + where a.id == item.id + select new { a.id, bid = b.id }).ToList(); + Assert.True(t3.Any()); + Assert.Equal(item.id, t3[0].id); + Assert.Equal(comment.id, t3[0].bid); + } + } +} diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs index 6df4e361..ab0b1e89 100644 --- a/FreeSql.Tests/UnitTest1.cs +++ b/FreeSql.Tests/UnitTest1.cs @@ -88,6 +88,18 @@ namespace FreeSql.Tests { [Fact] public void Test1() { + var linqto1 = + from p in g.sqlite.Select() + where p.Id >= 0 + // && p.OrderDetails.AsSelect().Where(c => c.Id > 10).Any() + orderby p.Id descending + orderby p.CustomerName ascending + select new { Name = p.CustomerName, Length = p.Id }; + + + + + var testddd = new TestEntity { Test = 22, diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs index 40b44ad8..9ac28e50 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs @@ -27,6 +27,7 @@ public static class FreeSqlGlobalExtensions { }); public static bool IsNumberType(this Type that) => that == null ? false : dicIsNumberType.Value.ContainsKey(that); public static bool IsNullableType(this Type that) => that?.FullName.StartsWith("System.Nullable`1[") == true; + public static bool IsAnonymousType(this Type that) => that?.FullName.StartsWith("<>f__AnonymousType") == true; internal static Type NullableTypeOrThis(this Type that) => that?.IsNullableType() == true ? that.GenericTypeArguments.First() : that; /// diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index 55698c46..e6e6f3ed 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.5.8 + 0.5.9 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 26420e77..79a22689 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -963,6 +963,31 @@ 閫夋嫨鍒 + + + 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + + + + + 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + + + + + 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + + + + + 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + + + + + 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + + 鎵цSQL鏌ヨ锛岃繑鍥炴寚瀹氬瓧娈电殑璁板綍鐨勭涓鏉¤褰曪紝璁板綍涓嶅瓨鍦ㄦ椂杩斿洖 TReturn 榛樿鍊 @@ -1291,6 +1316,11 @@ 閫夋嫨鍒 + + + 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + + 杩斿洖鍗冲皢鎵ц鐨凷QL璇彞 diff --git a/FreeSql/Interface/Curd/ISelect/ISelect1.cs b/FreeSql/Interface/Curd/ISelect/ISelect1.cs index 68e836d8..f9130f86 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect1.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect1.cs @@ -31,6 +31,27 @@ namespace FreeSql { List ToList(Expression> select); Task> ToListAsync(Expression> select); + /// + /// 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + /// + ISelect Select(Expression> select) where TReturn : class; + /// + /// 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + /// + ISelect Join(ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression> resultSelector) where TInner : class where TResult : class; + /// + /// 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + /// + ISelect GroupJoin(ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression, TResult>> resultSelector) where TInner : class where TResult : class; + /// + /// 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + /// + ISelect DefaultIfEmpty(); + /// + /// 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + /// + ISelect SelectMany(Expression>> collectionSelector, Expression> resultSelector) where TCollection : class where TResult : class; + /// /// 鎵цSQL鏌ヨ锛岃繑鍥炴寚瀹氬瓧娈电殑璁板綍鐨勭涓鏉¤褰曪紝璁板綍涓嶅瓨鍦ㄦ椂杩斿洖 TReturn 榛樿鍊 /// diff --git a/FreeSql/Interface/Curd/ISelect/ISelectGrouping.cs b/FreeSql/Interface/Curd/ISelect/ISelectGrouping.cs index f63f7803..17c8ec0f 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelectGrouping.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelectGrouping.cs @@ -36,6 +36,11 @@ namespace FreeSql { List ToList(Expression, TReturn>> select); Task> ToListAsync(Expression, TReturn>> select); + /// + /// 銆恖inq to sql銆戜笓鐢ㄦ柟娉曪紝涓嶅缓璁洿鎺ヤ娇鐢 + /// + List Select(Expression, TReturn>> select); + /// /// 杩斿洖鍗冲皢鎵ц鐨凷QL璇彞 /// diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 972b252e..9003906f 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -770,7 +770,7 @@ namespace FreeSql.Internal { if (mp?.Expression != null) { //瀵艰埅鏉′欢锛孫neToOne銆丮anyToOne var firstTb = tsc._tables.First().Table; var parentTb = _common.GetTableByEntity(mp.Expression.Type); - var parentTbRef = parentTb.GetTableRef(mp.Member.Name, tsc.style == ExpressionStyle.AsSelect); + var parentTbRef = parentTb?.GetTableRef(mp.Member.Name, tsc.style == ExpressionStyle.AsSelect); if (parentTbRef != null) { Expression navCondExp = null; for (var mn = 0; mn < parentTbRef.Columns.Count; mn++) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 6f997867..175aced0 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -31,6 +31,7 @@ namespace FreeSql.Internal.CommonProvider { protected DbConnection _connection; protected Action _trackToList; protected bool _distinct; + protected Expression _selectExpression; internal static void CopyData(Select0Provider from, object to, ReadOnlyCollection lambParms) { var toType = to?.GetType(); @@ -43,19 +44,22 @@ namespace FreeSql.Internal.CommonProvider { toType.GetField("_having", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._having); toType.GetField("_where", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new StringBuilder().Append(from._where.ToString())); toType.GetField("_params", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List(from._params.ToArray())); - //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++) { - 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 { - _multiTables[a].Alias = lambParms[a].Name; - _multiTables[a].Parameter = lambParms[a]; + if (lambParms == null) + toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List(from._tables.ToArray())); + else { + 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++) { + 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 { + _multiTables[a].Alias = lambParms[a].Name; + _multiTables[a].Parameter = lambParms[a]; + } } + if (_multiTables.Count < from._tables.Count) + _multiTables.AddRange(from._tables.GetRange(_multiTables.Count, from._tables.Count - _multiTables.Count)); } - if (_multiTables.Count < from._tables.Count) - _multiTables.AddRange(from._tables.GetRange(_multiTables.Count, from._tables.Count - _multiTables.Count)); toType.GetField("_tableRules", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._tableRules); toType.GetField("_join", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new StringBuilder().Append(from._join.ToString())); toType.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._cache); @@ -66,6 +70,7 @@ namespace FreeSql.Internal.CommonProvider { toType.GetField("_connection", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._connection); toType.GetField("_trackToList", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._trackToList); toType.GetField("_distinct", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._distinct); + toType.GetField("_selectExpression", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._selectExpression); } public Select0Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) { @@ -366,10 +371,14 @@ namespace FreeSql.Internal.CommonProvider { return ret; }); } - public List ToList(bool includeNestedMembers = false) => - this.ToListPrivate(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll()); - public Task> ToListAsync(bool includeNestedMembers = false) => - this.ToListPrivateAsync(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll()); + public List ToList(bool includeNestedMembers = false) { + if (_selectExpression != null) return this.InternalToList(_selectExpression); + return this.ToListPrivate(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll()); + } + public Task> ToListAsync(bool includeNestedMembers = false) { + if (_selectExpression != null) return this.InternalToListAsync(_selectExpression); + return this.ToListPrivateAsync(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll()); + } public T1 ToOne() { this.Limit(1); return this.ToList().FirstOrDefault(); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index feae7640..2f5cdd11 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -5,6 +5,7 @@ using System.Data; using System.Linq; using System.Linq.Expressions; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; namespace FreeSql.Internal.CommonProvider { @@ -153,6 +154,70 @@ namespace FreeSql.Internal.CommonProvider { return this.InternalToListAsync(select?.Body); } + public ISelect Select(Expression> select) where TReturn : class { + if (typeof(TReturn) == typeof(T1)) return this as ISelect; + _tables[0].Parameter = select.Parameters[0]; + _selectExpression = select.Body; + var ret = _orm.Select(); + Select0Provider, T1>.CopyData(this, ret, null); + return ret; + } + public ISelect Join(ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression> resultSelector) where TInner : class where TResult : class { + _tables[0].Parameter = resultSelector.Parameters[0]; + _commonExpression.ExpressionLambdaToSql(outerKeySelector, new CommonExpression.ExpTSC { _tables = _tables }); + this.InternalJoin(Expression.Lambda>( + Expression.Equal(outerKeySelector.Body, innerKeySelector.Body), + new[] { outerKeySelector.Parameters[0], innerKeySelector.Parameters[0] } + ), SelectTableInfoType.InnerJoin); + if (typeof(TResult) == typeof(T1)) return this as ISelect; + _selectExpression = resultSelector.Body; + var ret = _orm.Select() as Select1Provider; + Select0Provider, T1>.CopyData(this, ret, null); + return ret; + } + public ISelect GroupJoin(ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression, TResult>> resultSelector) where TInner : class where TResult : class { + _tables[0].Parameter = resultSelector.Parameters[0]; + _commonExpression.ExpressionLambdaToSql(outerKeySelector, new CommonExpression.ExpTSC { _tables = _tables }); + this.InternalJoin(Expression.Lambda>( + Expression.Equal(outerKeySelector.Body, innerKeySelector.Body), + new[] { outerKeySelector.Parameters[0], innerKeySelector.Parameters[0] } + ), SelectTableInfoType.InnerJoin); + if (typeof(TResult) == typeof(T1)) return this as ISelect; + _selectExpression = resultSelector.Body; + var ret = _orm.Select() as Select1Provider; + Select0Provider, T1>.CopyData(this, ret, null); + return ret; + } + public ISelect SelectMany(Expression>> collectionSelector, Expression> resultSelector) where TCollection : class where TResult : class { + SelectTableInfo find = null; + if (collectionSelector.Body.NodeType == ExpressionType.Call) { + var callExp = collectionSelector.Body as MethodCallExpression; + if (callExp.Method.Name == "DefaultIfEmpty" && callExp.Object.Type.GenericTypeArguments.Any()) { + find = _tables.Where((a, idx) => idx > 0 && a.Type == SelectTableInfoType.InnerJoin && a.Table.Type == callExp.Object.Type.GenericTypeArguments[0]).LastOrDefault(); + if (find != null) { + if (!string.IsNullOrEmpty(find.On)) find.On = Regex.Replace(find.On, $@"\b{find.Alias}\.", $"{resultSelector.Parameters[1].Name}."); + if (!string.IsNullOrEmpty(find.NavigateCondition)) find.NavigateCondition = Regex.Replace(find.NavigateCondition, $@"\b{find.Alias}\.", $"{resultSelector.Parameters[1].Name}."); + find.Type = SelectTableInfoType.LeftJoin; + find.Alias = resultSelector.Parameters[1].Name; + find.Parameter = resultSelector.Parameters[1]; + } + } + } + if (find == null) { + var tb = _commonUtils.GetTableByEntity(typeof(TCollection)); + if (tb == null) throw new Exception($"SelectMany 閿欒鐨勭被鍨嬶細{typeof(TCollection).FullName}"); + _tables.Add(new SelectTableInfo { Alias = resultSelector.Parameters[1].Name, AliasInit = resultSelector.Parameters[1].Name, Parameter = resultSelector.Parameters[1], Table = tb, Type = SelectTableInfoType.From }); + } + if (typeof(TResult) == typeof(T1)) return this as ISelect; + _selectExpression = resultSelector.Body; + var ret = _orm.Select() as Select1Provider; + Select0Provider, T1>.CopyData(this, ret, null); + return ret; + } + public ISelect DefaultIfEmpty() { + return this; + } + public DataTable ToDataTable(Expression> select) { if (select == null) return this.InternalToDataTable(select?.Body); _tables[0].Parameter = select.Parameters[0]; diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs index dc139735..075235b7 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs @@ -110,6 +110,7 @@ namespace FreeSql.Internal.CommonProvider { method = method.MakeGenericMethod(typeof(TReturn)); return method.Invoke(_select, new object[] { (map, field.Length > 0 ? field.Remove(0, 2).ToString() : null) }) as Task>; } + public List Select(Expression, TReturn>> select) => ToList(select); public string ToSql(Expression, TReturn>> select) { var map = new ReadAnonymousTypeInfo(); diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 065ef133..24dc24ac 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -18,7 +18,7 @@ namespace FreeSql.Internal { static ConcurrentDictionary> _cacheGetTableByEntity = new ConcurrentDictionary>(); internal static void RemoveTableByEntity(Type entity, CommonUtils common) { - if (entity.FullName.StartsWith("<>f__AnonymousType") || + if (entity.IsAnonymousType() || entity.IsValueType || entity.IsNullableType() || entity.NullableTypeOrThis() == typeof(BigInteger) @@ -27,7 +27,7 @@ namespace FreeSql.Internal { if (tbc.TryRemove(entity, out var trytb) && trytb?.TypeLazy != null) tbc.TryRemove(trytb.TypeLazy, out var trylz); } internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) { - if (entity.FullName.StartsWith("<>f__AnonymousType") || + if (entity.IsAnonymousType() || entity.IsValueType || entity.IsNullableType() || entity.NullableTypeOrThis() == typeof(BigInteger) diff --git a/FreeSql/Sqlite/SqliteCodeFirst.cs b/FreeSql/Sqlite/SqliteCodeFirst.cs index ce503cc8..b4756b1e 100644 --- a/FreeSql/Sqlite/SqliteCodeFirst.cs +++ b/FreeSql/Sqlite/SqliteCodeFirst.cs @@ -254,7 +254,7 @@ namespace FreeSql.Sqlite { public bool SyncStructure() => this.SyncStructure(typeof(TEntity)); public bool SyncStructure(params Type[] entityTypes) { if (entityTypes == null) return true; - var syncTypes = entityTypes.Where(a => dicSyced.ContainsKey(a.FullName) == false).ToArray(); + var syncTypes = entityTypes.Where(a => a.IsAnonymousType() == false && dicSyced.ContainsKey(a.FullName) == false).ToArray(); if (syncTypes.Any() == false) return true; var before = new Aop.SyncStructureBeforeEventArgs(entityTypes); _orm.Aop.SyncStructureBefore?.Invoke(this, before);