From d9de8e986bb9ff2e368f3a615630e70830c69ae1 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Wed, 20 Mar 2019 22:47:21 +0800 Subject: [PATCH] ## v0.3.20 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 ToList 选择指定对象时,应附加所有字段查询返回; - 修复 Lazy 延时类与实体关系冲突 bug; - 修复 附加对象读取时,记录为空应该返回null,而不是返回非null(字段默认值)对象; --- .../Properties/launchSettings.json | 27 ++++ FreeSql.DbContext/DbSet.cs | 35 ++++- FreeSql.DbContext/FreeSql.DbContext.csproj | 2 +- FreeSql.Repository/FreeSql.Repository.csproj | 2 +- FreeSql.Tests/UnitTest1.cs | 8 +- FreeSql/FreeSql.csproj | 2 +- FreeSql/Internal/CommonExpression.cs | 75 ++++++--- .../CommonProvider/AdoProvider/AdoProvider.cs | 3 +- .../AdoProvider/AdoProviderAsync.cs | 2 +- .../SelectProvider/Select0Provider.cs | 18 ++- .../Internal/Model/ReadAnonymousTypeInfo.cs | 1 + FreeSql/Internal/UtilsExpressionTree.cs | 145 +++++++++++++----- FreeSql/MySql/MySqlAdo/MySqlAdo.cs | 3 +- FreeSql/Oracle/OracleAdo/OracleAdo.cs | 4 +- .../PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs | 4 +- .../SqlServer/SqlServerAdo/SqlServerAdo.cs | 4 +- FreeSql/Sqlite/SqliteAdo/SqliteAdo.cs | 4 +- 17 files changed, 252 insertions(+), 87 deletions(-) create mode 100644 Examples/dbcontext_01/Properties/launchSettings.json diff --git a/Examples/dbcontext_01/Properties/launchSettings.json b/Examples/dbcontext_01/Properties/launchSettings.json new file mode 100644 index 00000000..a7caa4d3 --- /dev/null +++ b/Examples/dbcontext_01/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:53030/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "dbcontext_01": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:53031/" + } + } +} \ No newline at end of file diff --git a/FreeSql.DbContext/DbSet.cs b/FreeSql.DbContext/DbSet.cs index 012ef7ae..7bbbde9e 100644 --- a/FreeSql.DbContext/DbSet.cs +++ b/FreeSql.DbContext/DbSet.cs @@ -1,7 +1,9 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Data; using System.Data.Common; +using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; @@ -18,8 +20,39 @@ namespace FreeSql { public IInsert Insert(IEnumerable source) => _ctx._fsql.Insert(source).WithTransaction(_ctx.GetOrBeginTransaction()); public IUpdate Update => _ctx._fsql.Update().WithTransaction(_ctx.GetOrBeginTransaction()); - public IDelete Delete => _ctx._fsql.Delete().WithTransaction(_ctx.GetOrBeginTransaction()); + + //protected Dictionary _vals = new Dictionary(); + //protected tableinfo + + //public void Add(TEntity source) { + + //} + //public void AddRange(TEntity[] source) { + + //} + //public void AddRange(IEnumerable source) { + + //} + //public void Update(TEntity source) { + + //} + //public void UpdateRange(TEntity[] source) { + + //} + //public void UpdateRange(IEnumerable source) { + + //} + //public void Remove(TEntity source) { + + //} + //public void RemoveRange(TEntity[] source) { + + //} + //public void RemoveRange(IEnumerable source) { + + //} + } internal class BaseDbSet : DbSet where TEntity : class { diff --git a/FreeSql.DbContext/FreeSql.DbContext.csproj b/FreeSql.DbContext/FreeSql.DbContext.csproj index df3b6775..06d1b694 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.csproj +++ b/FreeSql.DbContext/FreeSql.DbContext.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.19 + 0.3.20 true YeXiangQin FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite. diff --git a/FreeSql.Repository/FreeSql.Repository.csproj b/FreeSql.Repository/FreeSql.Repository.csproj index 2a22fd1e..46f51c60 100644 --- a/FreeSql.Repository/FreeSql.Repository.csproj +++ b/FreeSql.Repository/FreeSql.Repository.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.19 + 0.3.20 YeXiangQin FreeSql Implementation of General Repository, Support MySql/SqlServer/PostgreSQL/Oracle/Sqlite, and read/write separation、and split table. https://github.com/2881099/FreeSql/wiki/Repository diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs index 41fabd6c..6d9cd10b 100644 --- a/FreeSql.Tests/UnitTest1.cs +++ b/FreeSql.Tests/UnitTest1.cs @@ -12,7 +12,7 @@ namespace FreeSql.Tests { public class UnitTest1 { public class Order { - [Column(IsPrimary = true)] + [Column(IsIdentity = true)] public int Id { get; set; } public string OrderTitle { get; set; } public string CustomerName { get; set; } @@ -20,7 +20,7 @@ namespace FreeSql.Tests { public virtual List OrderDetails { get; set; } } public class OrderDetail { - [Column(IsPrimary = true)] + [Column(IsIdentity = true)] public int Id { get; set; } public int OrderId { get; set; } @@ -34,6 +34,10 @@ namespace FreeSql.Tests { public DbSet Orders { get; set; } public DbSet OrderDetails { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder builder) { + builder.UseFreeSql(g.mysql); + } } [Fact] diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index 48907e4b..babb3fd9 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.19 + 0.3.20 true YeXiangQin FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite. diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 88bce447..3c538f94 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -54,16 +54,19 @@ namespace FreeSql.Internal { field.Append(", ").Append(parent.DbField); if (index >= 0) field.Append(" as").Append(++index); return false; + case ExpressionType.Parameter: case ExpressionType.MemberAccess: if (_common.GetTableByEntity(exp.Type) != null) { //加载表所有字段 var map = new List(); ExpressionSelectColumn_MemberAccess(_tables, map, SelectTableInfoType.From, exp, true, getSelectGroupingMapString); - parent.Consturctor = map.First().Table.Table.Type.GetConstructor(new Type[0]); + var tb = parent.Table = map.First().Table.Table; + parent.Consturctor = tb.Type.GetConstructor(new Type[0]); parent.ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties; for (var idx = 0; idx < map.Count; idx++) { var child = new ReadAnonymousTypeInfo { - Property = map.First().Table.Table.Type.GetProperty(map[idx].Column.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance), - CsName = map[idx].Column.CsName, DbField = $"{map[idx].Table.Alias}.{_common.QuoteSqlName(map[idx].Column.Attribute.Name)}" }; + Property = tb.Properties.TryGetValue(map[idx].Column.CsName, out var tryprop) ? tryprop : tb.Type.GetProperty(map[idx].Column.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance), + CsName = map[idx].Column.CsName, DbField = $"{map[idx].Table.Alias}.{_common.QuoteSqlName(map[idx].Column.Attribute.Name)}" + }; field.Append(", ").Append(_common.QuoteReadColumn(map[idx].Column.CsType, child.DbField)); if (index >= 0) field.Append(" as").Append(++index); parent.Childs.Add(child); @@ -82,7 +85,8 @@ namespace FreeSql.Internal { for (var a = 0; a < newExp.Members.Count; a++) { var child = new ReadAnonymousTypeInfo { Property = newExp.Type.GetProperty(newExp.Members[a].Name, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance), - CsName = newExp.Members[a].Name, CsType = newExp.Arguments[a].Type }; + CsName = newExp.Members[a].Name, CsType = newExp.Arguments[a].Type + }; parent.Childs.Add(child); ReadAnonymousField(_tables, field, child, ref index, newExp.Arguments[a], getSelectGroupingMapString); } @@ -93,22 +97,36 @@ namespace FreeSql.Internal { if (index >= 0) field.Append(" as").Append(++index); return false; } - internal object ReadAnonymous(ReadAnonymousTypeInfo parent, DbDataReader dr, ref int index) { - if (parent.Childs.Any() == false) return dr.GetValue(++index); + internal object ReadAnonymous(ReadAnonymousTypeInfo parent, DbDataReader dr, ref int index, bool notRead) { + if (parent.Childs.Any() == false) { + if (notRead) { + ++index; + return null; + } + return dr.GetValue(++index); + } switch (parent.ConsturctorType) { case ReadAnonymousTypeInfoConsturctorType.Arguments: var args = new object[parent.Childs.Count]; for (var a = 0; a < parent.Childs.Count; a++) { - args[a] = Utils.GetDataReaderValue(parent.Childs[a].CsType, ReadAnonymous(parent.Childs[a], dr, ref index)); + var objval = ReadAnonymous(parent.Childs[a], dr, ref index, notRead); + if (notRead == false) + args[a] = Utils.GetDataReaderValue(parent.Childs[a].CsType, objval); } return parent.Consturctor.Invoke(args); case ReadAnonymousTypeInfoConsturctorType.Properties: var ret = parent.Consturctor.Invoke(null); + var isnull = notRead; for (var b = 0; b < parent.Childs.Count; b++) { var prop = parent.Childs[b].Property; - prop.SetValue(ret, Utils.GetDataReaderValue(prop.PropertyType, ReadAnonymous(parent.Childs[b], dr, ref index)), null); + var objval = ReadAnonymous(parent.Childs[b], dr, ref index, notRead); + var safeval = Utils.GetDataReaderValue(prop.PropertyType, objval); + if (isnull == false && safeval == null && parent.Table.ColumnsByCs.TryGetValue(parent.Childs[b].CsName, out var trycol) && trycol.Attribute.IsPrimary) + isnull = true; + if (isnull == false) + prop.SetValue(ret, safeval, null); } - return ret; + return isnull ? null : ret; } return null; } @@ -445,24 +463,26 @@ namespace FreeSql.Internal { var other3Exp = ExpressionLambdaToSqlOther(exp3, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (string.IsNullOrEmpty(other3Exp) == false) return other3Exp; throw new Exception($"未实现函数表达式 {exp3} 解析"); + case ExpressionType.Parameter: case ExpressionType.MemberAccess: var exp4 = exp as MemberExpression; - if (exp4.Expression != null && exp4.Expression.Type.IsArray == false && exp4.Expression.Type.IsNullableType()) return ExpressionLambdaToSql(exp4.Expression, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); - var extRet = ""; - var memberType = exp4.Expression?.Type ?? exp4.Type; - switch (memberType.FullName) { - case "System.String": extRet = ExpressionLambdaToSqlMemberAccessString(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); break; - case "System.DateTime": extRet = ExpressionLambdaToSqlMemberAccessDateTime(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); break; - case "System.TimeSpan": extRet = ExpressionLambdaToSqlMemberAccessTimeSpan(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); break; + if (exp4 != null) { + if (exp4.Expression != null && exp4.Expression.Type.IsArray == false && exp4.Expression.Type.IsNullableType()) return ExpressionLambdaToSql(exp4.Expression, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var extRet = ""; + var memberType = exp4.Expression?.Type ?? exp4.Type; + switch (memberType.FullName) { + case "System.String": extRet = ExpressionLambdaToSqlMemberAccessString(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); break; + case "System.DateTime": extRet = ExpressionLambdaToSqlMemberAccessDateTime(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); break; + case "System.TimeSpan": extRet = ExpressionLambdaToSqlMemberAccessTimeSpan(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); break; + } + if (string.IsNullOrEmpty(extRet) == false) return extRet; + var other4Exp = ExpressionLambdaToSqlOther(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + if (string.IsNullOrEmpty(other4Exp) == false) return other4Exp; } - if (string.IsNullOrEmpty(extRet) == false) return extRet; - var other4Exp = ExpressionLambdaToSqlOther(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); - if (string.IsNullOrEmpty(other4Exp) == false) return other4Exp; - var expStack = new Stack(); expStack.Push(exp); MethodCallExpression callExp = null; - var exp2 = exp4.Expression; + var exp2 = exp4?.Expression; while (true) { switch(exp2?.NodeType) { case ExpressionType.Constant: @@ -508,7 +528,11 @@ namespace FreeSql.Internal { } Func getOrAddTable = (tbtmp, alias, isa, parmExp, mp) => { var finds = new SelectTableInfo[0]; - if (isa && parmExp != null) + if (_selectColumnMap != null) { + finds = _tables.Where(a => a.Table.Type == tbtmp.Type).ToArray(); + if (finds.Any()) finds = new[] { finds.First() }; + } + if (finds.Length != 1 && isa && parmExp != null) finds = _tables.Where(a => a.Parameter == parmExp).ToArray(); if (finds.Length != 1) { var navdot = string.IsNullOrEmpty(alias) ? new SelectTableInfo[0] : _tables.Where(a2 => a2.Parameter != null && alias.StartsWith($"{a2.Alias}__")).ToArray(); @@ -605,6 +629,13 @@ namespace FreeSql.Internal { alias2 = find2.Alias; tb2 = tb2tmp; } + if (exp2.NodeType == ExpressionType.Parameter && expStack.Any() == false) { //附加选择的参数所有列 + if (_selectColumnMap != null) { + foreach (var tb2c in tb2.Columns.Values) + _selectColumnMap.Add(new SelectColumnInfo { Table = find2, Column = tb2c }); + if (tb2.Columns.Any()) return ""; + } + } if (mp2 == null || expStack.Any()) continue; if (tb2.ColumnsByCs.ContainsKey(mp2.Member.Name) == false) { //如果选的是对象,附加所有列 if (_selectColumnMap != null) { diff --git a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs index 1f6007bd..eccbb611 100644 --- a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs +++ b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs @@ -26,6 +26,7 @@ namespace FreeSql.Internal.CommonProvider { public DataType DataType { get; } protected ICache _cache { get; set; } protected ILogger _log { get; set; } + protected CommonUtils _util { get; set; } protected int slaveUnavailables = 0; private object slaveLock = new object(); private Random slaveRandom = new Random(); @@ -89,7 +90,7 @@ namespace FreeSql.Internal.CommonProvider { dic.Add(dr.GetName(a), a); indexes = props.Select(a => dic.TryGetValue(a.Name, out var tryint) ? tryint : -1).ToArray(); } - ret.Add((T)Utils.ExecuteArrayRowReadClassOrTuple(type, indexes, dr, 0).Value); + ret.Add((T)Utils.ExecuteArrayRowReadClassOrTuple(type, indexes, dr, 0, _util).Value); }, cmdType, cmdText, cmdParms); return ret; } diff --git a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs index 9f5b8d70..dd78aa95 100644 --- a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs +++ b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs @@ -25,7 +25,7 @@ namespace FreeSql.Internal.CommonProvider { dic.Add(dr.GetName(a), a); indexes = props.Select(a => dic.TryGetValue(a.Name, out var tryint) ? tryint : -1).ToArray(); } - ret.Add((T)Utils.ExecuteArrayRowReadClassOrTuple(type, indexes, dr, 0).Value); + ret.Add((T)Utils.ExecuteArrayRowReadClassOrTuple(type, indexes, dr, 0, _util).Value); return Task.CompletedTask; }, cmdType, cmdText, cmdParms); return ret; diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 2a36ad31..4f33f1ac 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Data.Common; +using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -211,7 +212,7 @@ namespace FreeSql.Internal.CommonProvider { List ret = new List(); Type type = typeof(TTuple); _orm.Ado.ExecuteReader(_transaction, dr => { - var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr); + var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr, 0, _commonUtils); ret.Add((TTuple)read.Value); }, CommandType.Text, sql, _params.ToArray()); return ret; @@ -225,7 +226,7 @@ namespace FreeSql.Internal.CommonProvider { List ret = new List(); Type type = typeof(TTuple); await _orm.Ado.ExecuteReaderAsync(_transaction, dr => { - var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr); + var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr, 0, _commonUtils); ret.Add((TTuple)read.Value); return Task.CompletedTask; }, CommandType.Text, sql, _params.ToArray()); @@ -280,7 +281,7 @@ namespace FreeSql.Internal.CommonProvider { Type type = typeof(TReturn); _orm.Ado.ExecuteReader(_transaction, dr => { var index = -1; - ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index)); + ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index, false)); }, CommandType.Text, sql, _params.ToArray()); return ret; }); @@ -294,7 +295,7 @@ namespace FreeSql.Internal.CommonProvider { Type type = typeof(TReturn); await _orm.Ado.ExecuteReaderAsync(_transaction, dr => { var index = -1; - ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index)); + ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index, false)); return Task.CompletedTask; }, CommandType.Text, sql, _params.ToArray()); return ret; @@ -387,15 +388,16 @@ namespace FreeSql.Internal.CommonProvider { Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { - readExpAssign = Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp }); + readExpAssign = Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) }); } } blockExp.AddRange(new Expression[] { Expression.Assign(readExp, readExpAssign), Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), Expression.Assign(dataIndexExp, readExpDataIndex)), - Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)), - Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)), + Expression.Call(typeof(Trace).GetMethod("WriteLine", new Type[]{typeof(string)}), Expression.Call(typeof(string).GetMethod("Concat", new Type[]{typeof(object) }), readExpValue)), + Expression.IfThen(Expression.NotEqual(readExpValue, Expression.Constant(null)), + //Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)), Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType))) }); } @@ -403,7 +405,7 @@ namespace FreeSql.Internal.CommonProvider { blockExp.Clear(); blockExp.AddRange(new Expression[] { Expression.Assign(dataIndexExp, Expression.Constant(0)), - Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(type), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp })), + Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(type), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) })), Expression.Assign(retExp, Expression.Convert(readExpValue, type)) }); } diff --git a/FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs b/FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs index 33c4cf53..7b35a8ae 100644 --- a/FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs +++ b/FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs @@ -12,6 +12,7 @@ namespace FreeSql.Internal.Model { public ConstructorInfo Consturctor { get; set; } public ReadAnonymousTypeInfoConsturctorType ConsturctorType { get; set; } public List Childs = new List(); + public TableInfo Table { get; set; } } enum ReadAnonymousTypeInfoConsturctorType { Arguments, Properties } } diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 673e5b5b..1c15a85a 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -19,7 +19,7 @@ namespace FreeSql.Internal { internal static void RemoveTableByEntity(Type entity, CommonUtils common) { if (entity.FullName.StartsWith("<>f__AnonymousType")) return; var tbc = _cacheGetTableByEntity.GetOrAdd(common._orm.Ado.DataType, k1 => new ConcurrentDictionary()); //区分数据库类型缓存 - tbc.TryRemove(entity, out var trytb); + 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")) return null; @@ -573,6 +573,7 @@ namespace FreeSql.Internal { var type = assembly.DefinedTypes.Where(a => a.FullName.EndsWith(trytbTypeLazyName)).FirstOrDefault(); trytb.TypeLazy = type; trytb.TypeLazySetOrm = type.GetProperty("__fsql_orm__", BindingFlags.Instance | BindingFlags.NonPublic).GetSetMethod(true); + tbc.AddOrUpdate(type, trytb, (oldkey, oldval) => trytb); } #endregion @@ -648,7 +649,7 @@ namespace FreeSql.Internal { //[typeof(JObject)] = true, //[typeof(JArray)] = true, }; - internal static ConcurrentDictionary> _dicExecuteArrayRowReadClassOrTuple = new ConcurrentDictionary>(); + internal static ConcurrentDictionary> _dicExecuteArrayRowReadClassOrTuple = new ConcurrentDictionary>(); internal class RowInfo { public object Value { get; set; } public int DataIndex { get; set; } @@ -661,31 +662,32 @@ namespace FreeSql.Internal { public static PropertyInfo PropertyDataIndex = typeof(RowInfo).GetProperty("DataIndex"); } internal static MethodInfo MethodDataReaderGetValue = typeof(DbDataReader).GetMethod("GetValue"); - internal static RowInfo ExecuteArrayRowReadClassOrTuple(Type type, int[] indexes, DbDataReader row, int dataIndex = 0) { + internal static RowInfo ExecuteArrayRowReadClassOrTuple(Type type, int[] indexes, DbDataReader row, int dataIndex, CommonUtils _commonUtils) { var func = _dicExecuteArrayRowReadClassOrTuple.GetOrAdd(type, s => { var returnTarget = Expression.Label(typeof(RowInfo)); var typeExp = Expression.Parameter(typeof(Type), "type"); var indexesExp = Expression.Parameter(typeof(int[]), "indexes"); var rowExp = Expression.Parameter(typeof(DbDataReader), "row"); var dataIndexExp = Expression.Parameter(typeof(int), "dataIndex"); + var commonUtilExp = Expression.Parameter(typeof(CommonUtils), "commonUtil"); - if (type.IsArray) return Expression.Lambda>( + if (type.IsArray) return Expression.Lambda>( Expression.New(RowInfo.Constructor, GetDataReaderValueBlockExpression(type, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), //Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) - ), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); + ), new[] { typeExp, indexesExp, rowExp, dataIndexExp, commonUtilExp }).Compile(); var typeGeneric = type; if (typeGeneric.IsNullableType()) typeGeneric = type.GenericTypeArguments.First(); if (typeGeneric.IsEnum || dicExecuteArrayRowReadClassOrTuple.ContainsKey(typeGeneric)) - return Expression.Lambda>( + return Expression.Lambda>( Expression.New(RowInfo.Constructor, GetDataReaderValueBlockExpression(type, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), //Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) - ), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); + ), new[] { typeExp, indexesExp, rowExp, dataIndexExp, commonUtilExp }).Compile(); if (type.Namespace == "System" && (type.FullName == "System.String" || type.IsValueType)) { //值类型,或者元组 bool isTuple = type.Name.StartsWith("ValueTuple`"); @@ -714,7 +716,7 @@ namespace FreeSql.Internal { Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { - read2ExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(field.FieldType), indexesExp, rowExp, dataIndexExp }); + read2ExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(field.FieldType), indexesExp, rowExp, dataIndexExp, commonUtilExp }); } } block2Exp.AddRange(new Expression[] { @@ -741,11 +743,11 @@ namespace FreeSql.Internal { Expression.Return(returnTarget, Expression.New(RowInfo.Constructor, Expression.Convert(ret2Exp, typeof(object)), dataIndexExp)), Expression.Label(returnTarget, Expression.Default(typeof(RowInfo))) }); - return Expression.Lambda>( - Expression.Block(new[] { ret2Exp, read2Exp }, block2Exp), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); + return Expression.Lambda>( + Expression.Block(new[] { ret2Exp, read2Exp }, block2Exp), new[] { typeExp, indexesExp, rowExp, dataIndexExp, commonUtilExp }).Compile(); } var rowLenExp = Expression.ArrayLength(rowExp); - return Expression.Lambda>( + return Expression.Lambda>( Expression.Block( Expression.IfThen( Expression.LessThan(dataIndexExp, rowLenExp), @@ -755,11 +757,11 @@ namespace FreeSql.Internal { Expression.Add(dataIndexExp, Expression.Constant(1)))) ), Expression.Label(returnTarget, Expression.Default(typeof(RowInfo))) - ), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); + ), new[] { typeExp, indexesExp, rowExp, dataIndexExp, commonUtilExp }).Compile(); } if (type == typeof(object) && indexes != null) { - Func dynamicFunc = (type2, indexes2, row2, dataindex2) => { + Func dynamicFunc = (type2, indexes2, row2, dataindex2, commonUtils2) => { dynamic expando = new System.Dynamic.ExpandoObject(); //动态类型字段 可读可写 var expandodic = (IDictionary)expando; var fc = row2.FieldCount; @@ -771,6 +773,7 @@ namespace FreeSql.Internal { } //类注入属性 + var typetb = GetTableByEntity(type, _commonUtils); var retExp = Expression.Variable(type, "ret"); var readExp = Expression.Variable(typeof(RowInfo), "read"); var readExpValue = Expression.MakeMemberAccess(readExp, RowInfo.PropertyValue); @@ -778,12 +781,18 @@ namespace FreeSql.Internal { var readExpValueParms = new List(); var readExpsIndex = Expression.Variable(typeof(int), "readsIndex"); var tryidxExp = Expression.Variable(typeof(int), "tryidx"); - var indexesLengthExp = Expression.Parameter(typeof(int), "indexesLength"); + var readpknullExp = Expression.Variable(typeof(bool), "isnull2"); + var readpkvalExp = Expression.Variable(typeof(object), "isnull3val"); + var indexesLengthExp = Expression.Variable(typeof(int), "indexesLength"); var blockExp = new List(); var ctor = type.GetConstructor(new Type[0]) ?? type.GetConstructors().First(); var ctorParms = ctor.GetParameters(); if (ctorParms.Length > 0) { + blockExp.AddRange(new Expression[] { + Expression.Assign(readpknullExp, Expression.Constant(false)) + }); foreach (var ctorParm in ctorParms) { + var ispkExp = new List(); Expression readExpAssign = null; //加速缓存 if (ctorParm.ParameterType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor, GetDataReaderValueBlockExpression(ctorParm.ParameterType, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), @@ -794,30 +803,64 @@ namespace FreeSql.Internal { var proptypeGeneric = ctorParm.ParameterType; if (proptypeGeneric.IsNullableType()) proptypeGeneric = proptypeGeneric.GenericTypeArguments.First(); if (proptypeGeneric.IsEnum || - dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) readExpAssign = Expression.New(RowInfo.Constructor, - GetDataReaderValueBlockExpression(ctorParm.ParameterType, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), + dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) { + + //判断主键为空,则整个对象不读取 + blockExp.Add(Expression.Assign(readpkvalExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp))); + if (typetb.ColumnsByCs.TryGetValue(ctorParm.Name, out var trycol) && trycol.Attribute.IsPrimary) { + ispkExp.Add( + Expression.IfThen( + Expression.And( + Expression.IsFalse(readpknullExp), + Expression.Or( + Expression.Equal(readpkvalExp, Expression.Constant(DBNull.Value)), + Expression.Equal(readpkvalExp, Expression.Constant(null)) + ) + ), + Expression.Assign(readpknullExp, Expression.Constant(true)) + ) + ); + } + + readExpAssign = Expression.New(RowInfo.Constructor, + GetDataReaderValueBlockExpression(ctorParm.ParameterType, readpkvalExp), //Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(ctorParm.ParameterType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) - ); - else { + ); + } else { readExpAssign = Expression.New(RowInfo.Constructor, - Expression.MakeMemberAccess(Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(ctorParm.ParameterType), indexesExp, rowExp, dataIndexExp }), RowInfo.PropertyValue), + Expression.MakeMemberAccess(Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(ctorParm.ParameterType), indexesExp, rowExp, dataIndexExp, commonUtilExp }), RowInfo.PropertyValue), Expression.Add(dataIndexExp, Expression.Constant(1))); } } var varctorParm = Expression.Variable(ctorParm.ParameterType, $"ctorParm{ctorParm.Name}"); readExpValueParms.Add(varctorParm); + + ispkExp.Add( + Expression.IfThen( + Expression.IsFalse(readpknullExp), + Expression.IfThenElse( + Expression.Equal(readExpValue, Expression.Constant(null)), + Expression.Assign(varctorParm, Expression.Default(ctorParm.ParameterType)), + Expression.Assign(varctorParm, Expression.Convert(readExpValue, ctorParm.ParameterType)) + ) + ) + ); blockExp.AddRange(new Expression[] { Expression.Assign(tryidxExp, dataIndexExp), Expression.Assign(readExp, readExpAssign), Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), - Expression.Assign(dataIndexExp, readExpDataIndex)), - Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)), - Expression.Assign(varctorParm, Expression.Default(ctorParm.ParameterType)), - Expression.Assign(varctorParm, Expression.Convert(readExpValue, ctorParm.ParameterType))) + Expression.Assign(dataIndexExp, readExpDataIndex) + ), + Expression.Block(ispkExp) }); } - blockExp.Add(Expression.Assign(retExp, Expression.New(ctor, readExpValueParms))); + blockExp.Add( + Expression.IfThen( + Expression.IsFalse(readpknullExp), + Expression.Assign(retExp, Expression.New(ctor, readExpValueParms)) + ) + ); } else { blockExp.AddRange(new Expression[] { Expression.Assign(retExp, Expression.New(ctor)), @@ -825,12 +868,14 @@ namespace FreeSql.Internal { Expression.IfThen( Expression.NotEqual(indexesExp, Expression.Constant(null)), Expression.Assign(indexesLengthExp, Expression.ArrayLength(indexesExp)) - ) + ), + Expression.Assign(readpknullExp, Expression.Constant(false)) }); var props = type.GetProperties();//.ToDictionary(a => a.Name, a => a, StringComparer.CurrentCultureIgnoreCase); var propIndex = 0; foreach (var prop in props) { + var ispkExp = new List(); var propGetSetMethod = prop.GetSetMethod(); Expression readExpAssign = null; //加速缓存 if (prop.PropertyType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor, @@ -842,17 +887,50 @@ namespace FreeSql.Internal { var proptypeGeneric = prop.PropertyType; if (proptypeGeneric.IsNullableType()) proptypeGeneric = proptypeGeneric.GenericTypeArguments.First(); if (proptypeGeneric.IsEnum || - dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) readExpAssign = Expression.New(RowInfo.Constructor, + dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) { + + //判断主键为空,则整个对象不读取 + blockExp.Add(Expression.Assign(readpkvalExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp))); + if (typetb.ColumnsByCs.TryGetValue(prop.Name, out var trycol) && trycol.Attribute.IsPrimary) { + ispkExp.Add( + Expression.IfThen( + Expression.And( + Expression.IsFalse(readpknullExp), + Expression.Or( + Expression.Equal(readpkvalExp, Expression.Constant(DBNull.Value)), + Expression.Equal(readpkvalExp, Expression.Constant(null)) + ) + ), + Expression.Block( + Expression.Assign(readpknullExp, Expression.Constant(true)), + Expression.Assign(retExp, Expression.Constant(null, type)) + ) + ) + ); + } + + readExpAssign = Expression.New(RowInfo.Constructor, GetDataReaderValueBlockExpression(prop.PropertyType, Expression.Call(rowExp, MethodDataReaderGetValue, tryidxExp)), //Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, MethodDataReaderGetValue, tryidxExp) }), Expression.Add(tryidxExp, Expression.Constant(1)) - ); - else { + ); + } else { ++propIndex; continue; //readExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), indexesExp, rowExp, tryidxExp }); } } + + ispkExp.Add( + Expression.IfThen( + Expression.IsFalse(readpknullExp), + Expression.IfThenElse( + Expression.Equal(readExpValue, Expression.Constant(null)), + Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)), + Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType)) + ) + ) + ); blockExp.AddRange(new Expression[] { //以下注释部分为【严格读取】,会损失一点性能,使用 select * from xxx 与属性映射赋值 Expression.IfThenElse( @@ -866,10 +944,7 @@ namespace FreeSql.Internal { Expression.Assign(readExp, readExpAssign), Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), Expression.Assign(dataIndexExp, readExpDataIndex)), - Expression.IfThenElse( - Expression.Equal(readExpValue, Expression.Constant(null)), - Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)), - Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType))) + Expression.Block(ispkExp) ) ) }); @@ -880,10 +955,10 @@ namespace FreeSql.Internal { Expression.Return(returnTarget, Expression.New(RowInfo.Constructor, retExp, dataIndexExp)), Expression.Label(returnTarget, Expression.Default(typeof(RowInfo))) }); - return Expression.Lambda>( - Expression.Block(new[] { retExp, readExp, tryidxExp, readExpsIndex, indexesLengthExp }.Concat(readExpValueParms), blockExp), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); + return Expression.Lambda>( + Expression.Block(new[] { retExp, readExp, tryidxExp, readpknullExp, readpkvalExp, readExpsIndex, indexesLengthExp }.Concat(readExpValueParms), blockExp), new[] { typeExp, indexesExp, rowExp, dataIndexExp, commonUtilExp }).Compile(); }); - return func(type, indexes, row, dataIndex); + return func(type, indexes, row, dataIndex, _commonUtils); } internal static MethodInfo MethodExecuteArrayRowReadClassOrTuple = typeof(Utils).GetMethod("ExecuteArrayRowReadClassOrTuple", BindingFlags.Static | BindingFlags.NonPublic); diff --git a/FreeSql/MySql/MySqlAdo/MySqlAdo.cs b/FreeSql/MySql/MySqlAdo/MySqlAdo.cs index 6f07ffb7..fe635fb9 100644 --- a/FreeSql/MySql/MySqlAdo/MySqlAdo.cs +++ b/FreeSql/MySql/MySqlAdo/MySqlAdo.cs @@ -10,11 +10,10 @@ using System.Threading; namespace FreeSql.MySql { class MySqlAdo : FreeSql.Internal.CommonProvider.AdoProvider { - CommonUtils _util; public MySqlAdo() : base(null, null, DataType.MySql) { } public MySqlAdo(CommonUtils util, ICache cache, ILogger log, string masterConnectionString, string[] slaveConnectionStrings) : base(cache, log, DataType.MySql) { - this._util = util; + base._util = util; MasterPool = new MySqlConnectionPool("主库", masterConnectionString, null, null); if (slaveConnectionStrings != null) { foreach (var slaveConnectionString in slaveConnectionStrings) { diff --git a/FreeSql/Oracle/OracleAdo/OracleAdo.cs b/FreeSql/Oracle/OracleAdo/OracleAdo.cs index 5449a36d..a8db437a 100644 --- a/FreeSql/Oracle/OracleAdo/OracleAdo.cs +++ b/FreeSql/Oracle/OracleAdo/OracleAdo.cs @@ -10,11 +10,9 @@ using System.Threading; namespace FreeSql.Oracle { class OracleAdo : FreeSql.Internal.CommonProvider.AdoProvider { - CommonUtils _util; - public OracleAdo() : base(null, null, DataType.Oracle) { } public OracleAdo(CommonUtils util, ICache cache, ILogger log, string masterConnectionString, string[] slaveConnectionStrings) : base(cache, log, DataType.Oracle) { - this._util = util; + base._util = util; MasterPool = new OracleConnectionPool("主库", masterConnectionString, null, null); if (slaveConnectionStrings != null) { foreach (var slaveConnectionString in slaveConnectionStrings) { diff --git a/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs b/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs index 722e0241..acafafc0 100644 --- a/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs +++ b/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs @@ -12,11 +12,9 @@ using System.Threading; namespace FreeSql.PostgreSQL { class PostgreSQLAdo : FreeSql.Internal.CommonProvider.AdoProvider { - CommonUtils _util; - public PostgreSQLAdo() : base(null, null, DataType.PostgreSQL) { } public PostgreSQLAdo(CommonUtils util, ICache cache, ILogger log, string masterConnectionString, string[] slaveConnectionStrings) : base(cache, log, DataType.PostgreSQL) { - this._util = util; + base._util = util; MasterPool = new PostgreSQLConnectionPool("主库", masterConnectionString, null, null); if (slaveConnectionStrings != null) { foreach (var slaveConnectionString in slaveConnectionStrings) { diff --git a/FreeSql/SqlServer/SqlServerAdo/SqlServerAdo.cs b/FreeSql/SqlServer/SqlServerAdo/SqlServerAdo.cs index 0d8698d8..3a57dff4 100644 --- a/FreeSql/SqlServer/SqlServerAdo/SqlServerAdo.cs +++ b/FreeSql/SqlServer/SqlServerAdo/SqlServerAdo.cs @@ -10,11 +10,9 @@ using System.Threading; namespace FreeSql.SqlServer { class SqlServerAdo : FreeSql.Internal.CommonProvider.AdoProvider { - CommonUtils _util; - public SqlServerAdo() : base(null, null, DataType.SqlServer) { } public SqlServerAdo(CommonUtils util, ICache cache, ILogger log, string masterConnectionString, string[] slaveConnectionStrings) : base(cache, log, DataType.SqlServer) { - this._util = util; + base._util = util; MasterPool = new SqlServerConnectionPool("主库", masterConnectionString, null, null); if (slaveConnectionStrings != null) { foreach (var slaveConnectionString in slaveConnectionStrings) { diff --git a/FreeSql/Sqlite/SqliteAdo/SqliteAdo.cs b/FreeSql/Sqlite/SqliteAdo/SqliteAdo.cs index 3a76f00f..67ad8eca 100644 --- a/FreeSql/Sqlite/SqliteAdo/SqliteAdo.cs +++ b/FreeSql/Sqlite/SqliteAdo/SqliteAdo.cs @@ -10,11 +10,9 @@ using System.Threading; namespace FreeSql.Sqlite { class SqliteAdo : FreeSql.Internal.CommonProvider.AdoProvider { - CommonUtils _util; - public SqliteAdo() : base(null, null, DataType.Sqlite) { } public SqliteAdo(CommonUtils util, ICache cache, ILogger log, string masterConnectionString, string[] slaveConnectionStrings) : base(cache, log, DataType.Sqlite) { - this._util = util; + base._util = util; MasterPool = new SqliteConnectionPool("主库", masterConnectionString, null, null); if (slaveConnectionStrings != null) { foreach (var slaveConnectionString in slaveConnectionStrings) {