From dc6f42cd68299409874e8caa9c308274f2eebb36 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 28 Nov 2020 04:16:58 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E4=BC=98=E5=8C=96=20=E5=8D=95=E8=A1=A8?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=20ExpressionTree=20=E6=80=A7=E8=83=BD?= =?UTF-8?q?=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Oracle/OracleCodeFirstTest.cs | 8 + FreeSql.sln | 1 + FreeSql/FreeSql.xml | 183 ++++++++++++++++++ .../SelectProvider/Select0ProviderReader.cs | 90 +++++++-- README.md | 29 ++- 5 files changed, 283 insertions(+), 28 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/Oracle/OracleCodeFirstTest.cs b/FreeSql.Tests/FreeSql.Tests/Oracle/OracleCodeFirstTest.cs index 78e554ef..1ce4f716 100644 --- a/FreeSql.Tests/FreeSql.Tests/Oracle/OracleCodeFirstTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/Oracle/OracleCodeFirstTest.cs @@ -51,6 +51,14 @@ namespace FreeSql.Tests.Oracle item2 = repo.Get(item1.Id); Assert.Equal(item1.CreatorId, item2.CreatorId); + + fsql.Aop.AuditDataReader -= fsql.Aop.AuditDataReaderHandler; + + item1 = new TS_SL361 { CreatorId = "" }; + repo.Insert(item1); + item2 = repo.Get(item1.Id); + + Assert.Null(item2.CreatorId); } } class TS_SNTE diff --git a/FreeSql.sln b/FreeSql.sln index fa28ac8e..fb6b29f9 100644 --- a/FreeSql.sln +++ b/FreeSql.sln @@ -8,6 +8,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{C6A74E2A-6660-473D-8852-B1D8348DB4E9}" ProjectSection(SolutionItems) = preProject readme.md = readme.md + README.zh-CN.md = README.zh-CN.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{94C8A78D-AA15-47B2-A348-530CD86BFC1B}" diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index a06a844f..f5d2d1db 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -3089,6 +3089,177 @@ + + + 测试数据库是否连接正确,本方法执行如下命令: + MySql/SqlServer/PostgreSQL/达梦/人大金仓/神通: SELECT 1 + Oracle: SELECT 1 FROM dual + + 命令超时设置(秒) + + true: 成功, false: 失败 + + + + 查询,若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】 + + + + + + + + + + 查询,ExecuteReaderAsync(dr => {}, "select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 查询 + + + + + + + + + 查询,ExecuteArrayAsync("select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 查询 + + + + + + + + + 查询,ExecuteDataSetAsync("select * from user where age > @age; select 2", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 查询 + + + + + + + + + 查询,ExecuteDataTableAsync("select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 在【主库】执行 + + + + + + + + + 在【主库】执行,ExecuteNonQueryAsync("delete from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 在【主库】执行 + + + + + + + + + 在【主库】执行,ExecuteScalarAsync("select 1 from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new SqlParameter { ParameterName = "age", Value = 25 }) + + + + + + + + + + + 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + + + 执行SQL返回对象集合,Query<User>("select * from user where age > @age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 }) + + + + + + + + + + + + 执行SQL返回对象集合,Query<User, Address>("select * from user where age > @age; select * from address", new { age = 25 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + + + + + + + 可自定义解析表达式 @@ -3917,6 +4088,12 @@ 超时 + + + 获取资源 + + + 使用完毕后,归还资源 @@ -3987,6 +4164,12 @@ 资源对象 + + + 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 + + 资源对象 + 归还对象给对象池的时候触发 diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs index 954eb0fc..ff4cf074 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs @@ -513,8 +513,14 @@ namespace FreeSql.Internal.CommonProvider }; }); } + static EventHandler _OldAuditDataReaderHandler; public GetAllFieldExpressionTreeInfo GetAllFieldExpressionTreeLevel2() { + if (_OldAuditDataReaderHandler != _orm.Aop.AuditDataReaderHandler) + { + _OldAuditDataReaderHandler = _orm.Aop.AuditDataReaderHandler; //清除单表 ExppressionTree + _dicGetAllFieldExpressionTree.TryRemove($"{_orm.Ado.DataType}-{_tables[0].Table.DbName}-{_tables[0].Alias}-{_tables[0].Type}", out var oldet); + } return _dicGetAllFieldExpressionTree.GetOrAdd(string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Alias}-{a.Type}")), s => { var tb1 = _tables.First().Table; @@ -621,18 +627,6 @@ namespace FreeSql.Internal.CommonProvider ) }); } - if (otherindex == 0) - { //不读导航属性,优化单表读取性能 - blockExp.Clear(); - blockExp.AddRange(new Expression[] { - Expression.Assign(dataIndexExp, Expression.Constant(0)), - Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(null, typeof(string)), Expression.Constant(type), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) })), - Expression.IfThen( - Expression.NotEqual(readExpValue, Expression.Constant(null)), - Expression.Assign(retExp, Expression.Convert(readExpValue, type)) - ) - }); - } if (tb1.TypeLazy != null) blockExp.Add( Expression.IfThen( @@ -640,6 +634,66 @@ namespace FreeSql.Internal.CommonProvider Expression.Call(retExp, tb1.TypeLazySetOrm, ormExp) ) ); //将 orm 传递给 lazy + if (otherindex == 0) + { + //不读导航属性,优化单表读取性能 + blockExp.Clear(); + blockExp.AddRange(new Expression[]{ + Expression.Assign(retExp, type.InternalNewExpression()), + Expression.Assign(dataIndexExp, Expression.Constant(0)) + }); + var colidx = 0; + foreach (var col in tb.Table.Columns.Values) + { + var drvalType = col.Attribute.MapType.NullableTypeOrThis(); + var propGetSetMethod = tb.Table.Properties[col.CsName].GetSetMethod(true); + if (col.CsType == col.Attribute.MapType && + _orm.Aop.AuditDataReaderHandler == null && + _dicMethodDataReaderGetValue.TryGetValue(col.Attribute.MapType.NullableTypeOrThis(), out var drGetValueMethod)) + { + Expression drvalExp = Expression.Call(rowExp, drGetValueMethod, Expression.Constant(colidx)); + if (col.CsType.IsNullableType()) drvalExp = Expression.Convert(drvalExp, col.CsType); + drvalExp = Expression.Condition(Expression.Call(rowExp, _MethodDataReaderIsDBNull, Expression.Constant(colidx)), Expression.Default(col.CsType), drvalExp); + + if (drvalType.IsArray || drvalType.IsEnum || Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(drvalType)) + { + var drvalExpCatch = Utils.GetDataReaderValueBlockExpression( + col.CsType, + Expression.Call(Utils.MethodDataReaderGetValue, new Expression[] { Expression.Constant(_commonUtils), rowExp, Expression.Constant(colidx) }) + ); + blockExp.Add(Expression.TryCatch( + Expression.Call(retExp, propGetSetMethod, drvalExp), + Expression.Catch(typeof(Exception), + Expression.Call(retExp, propGetSetMethod, Expression.Convert(drvalExpCatch, col.CsType)) + //Expression.Throw(Expression.Constant(new Exception($"{_commonUtils.QuoteSqlName(col.Attribute.Name)} is NULL,除非设置特性 [Column(IsNullable = false)]"))) + ))); + } + else + { + blockExp.Add(Expression.TryCatch( + Expression.Call(retExp, propGetSetMethod, drvalExp), + Expression.Catch(typeof(Exception), + Expression.Call(retExp, propGetSetMethod, Expression.Default(col.CsType)) + //Expression.Throw(Expression.Constant(new Exception($"{_commonUtils.QuoteSqlName(col.Attribute.Name)} is NULL,除非设置特性 [Column(IsNullable = false)]"))) + ))); + } + } + else + { + if (drvalType.IsArray || drvalType.IsEnum || Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(drvalType)) + { + var drvalExp = Utils.GetDataReaderValueBlockExpression( + col.CsType, + Expression.Call(Utils.MethodDataReaderGetValue, new Expression[] { Expression.Constant(_commonUtils), rowExp, Expression.Constant(colidx) }) + ); + blockExp.Add(Expression.Call(retExp, propGetSetMethod, Expression.Convert(drvalExp, col.CsType))); + } + } + colidx++; + } + if (tb1.TypeLazy != null) + blockExp.Add(Expression.Call(retExp, tb1.TypeLazySetOrm, ormExp)); //将 orm 传递给 lazy + } blockExp.AddRange(new Expression[] { Expression.Return(returnTarget, retExp), Expression.Label(returnTarget, Expression.Default(type)) @@ -651,6 +705,18 @@ namespace FreeSql.Internal.CommonProvider }; }); } + static MethodInfo _MethodDataReaderIsDBNull = typeof(DbDataReader).GetMethod("IsDBNull", new Type[] { typeof(int) }); + static Dictionary _dicMethodDataReaderGetValue = new Dictionary + { + [typeof(bool)] = typeof(DbDataReader).GetMethod("GetBoolean", new Type[] { typeof(int) }), + [typeof(int)] = typeof(DbDataReader).GetMethod("GetInt32", new Type[] { typeof(int) }), + [typeof(long)] = typeof(DbDataReader).GetMethod("GetInt64", new Type[] { typeof(int) }), + [typeof(double)] = typeof(DbDataReader).GetMethod("GetDouble", new Type[] { typeof(int) }), + [typeof(float)] = typeof(DbDataReader).GetMethod("GetFloat", new Type[] { typeof(int) }), + [typeof(decimal)] = typeof(DbDataReader).GetMethod("GetDecimal", new Type[] { typeof(int) }), + [typeof(DateTime)] = typeof(DbDataReader).GetMethod("GetDateTime", new Type[] { typeof(int) }), + [typeof(string)] = typeof(DbDataReader).GetMethod("GetString", new Type[] { typeof(int) }), + }; protected double InternalAvg(Expression exp) { diff --git a/README.md b/README.md index 6d677cfe..6b976a39 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,12 @@ FreeSql is a powerful O/RM component, supports .NET Core 2.1+, .NET Framework 4. -- 🛠 Support CodeFirst mode, support data migration even when using Access database. -- 💻 Support DbFirst mode, support import entity class from database, or use [Entity Class Generation Tool](https://github.com/2881099/FreeSql/wiki/DbFirst) to generate entity class. +- 🛠 Support CodeFirst data migration. +- 💻 Support DbFirst import entity class from database, or use [Generation Tool](https://github.com/2881099/FreeSql/wiki/DbFirst). - ⛳ Support advanced type mapping, such as PostgreSQL array type, etc. -- ✒ Support large number of expression functions, and highly customizable analysis. -- 🏁 Support one-to-many and many-to-many greedy loading of navigation properties, and lazy loading. -- 📃 Support Read/Write separation, Splitting Table/Database, Filters, Optimistic Locking and pessimistic locking. +- 🌲 Support expression functions, and customizable analysis. +- 🏁 Support one-to-many and many-to-many navigation properties, include and lazy loading. +- 📃 Support Read/Write separation, Splitting Table/Database, Global filters, Optimistic and pessimistic locker. - 🌳 Support MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/达梦/人大金仓/神舟通用/翰高/Access, etc. QQ Groups:4336577(full)、**8578575(available)**、**52508226(available)** @@ -28,25 +28,22 @@ QQ Groups:4336577(full)、**8578575(available)**、**52508226(available)** | | | | - | - | -| |  [Introduction](https://www.cnblogs.com/FreeSql/p/11531300.html)  \|  [Select](https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2)  \|  [Update](https://github.com/2881099/FreeSql/wiki/%e4%bf%ae%e6%94%b9)  \|  [Insert](https://github.com/2881099/FreeSql/wiki/%e6%b7%bb%e5%8a%a0)  \|  [Delete](https://github.com/2881099/FreeSql/wiki/%e5%88%a0%e9%99%a4)  | -| |  [Expression Functions](https://github.com/2881099/FreeSql/wiki/%e8%a1%a8%e8%be%be%e5%bc%8f%e5%87%bd%e6%95%b0)  \|  [CodeFirst](https://github.com/2881099/FreeSql/wiki/CodeFirst)  \|  [DbFirst](https://github.com/2881099/FreeSql/wiki/DbFirst)  \|  [Filters](https://github.com/2881099/FreeSql/wiki/%e8%bf%87%e6%bb%a4%e5%99%a8)  | -| |  [Repository](https://github.com/2881099/FreeSql/wiki/Repository)  \|  [UnitOfWork](https://github.com/2881099/FreeSql/wiki/%e5%b7%a5%e4%bd%9c%e5%8d%95%e5%85%83)  \|  [AOP](https://github.com/2881099/FreeSql/wiki/AOP)  \|  [DbContext](https://github.com/2881099/FreeSql/wiki/DbContext)  | -| |  [Read/Write Separation](https://github.com/2881099/FreeSql/wiki/%e8%af%bb%e5%86%99%e5%88%86%e7%a6%bb)  \|  [Splitting Table/Database](https://github.com/2881099/FreeSql/wiki/%e5%88%86%e8%a1%a8%e5%88%86%e5%ba%93)  \|  [Mysterious Technology](https://github.com/2881099/FreeSql/wiki/%E9%AA%9A%E6%93%8D%E4%BD%9C)  \|  [FAQ](https://github.com/dotnetcore/FreeSql/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)  \|  [*Update Notes*](https://github.com/2881099/FreeSql/wiki/%e6%9b%b4%e6%96%b0%e6%97%a5%e5%bf%97)  | +| [Select](https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2)  \|  [Update](https://github.com/2881099/FreeSql/wiki/%e4%bf%ae%e6%94%b9)  \|  [Insert](https://github.com/2881099/FreeSql/wiki/%e6%b7%bb%e5%8a%a0)  \|  [Delete](https://github.com/2881099/FreeSql/wiki/%e5%88%a0%e9%99%a4)  [FAQ](https://github.com/dotnetcore/FreeSql/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)  \|  [*Update Notes*](https://github.com/2881099/FreeSql/wiki/%e6%9b%b4%e6%96%b0%e6%97%a5%e5%bf%97)  | +| [Expression Functions](https://github.com/2881099/FreeSql/wiki/%e8%a1%a8%e8%be%be%e5%bc%8f%e5%87%bd%e6%95%b0)  \|  [CodeFirst](https://github.com/2881099/FreeSql/wiki/CodeFirst)  \|  [DbFirst](https://github.com/2881099/FreeSql/wiki/DbFirst)  \|  [Filters](https://github.com/2881099/FreeSql/wiki/%e8%bf%87%e6%bb%a4%e5%99%a8)  | +| [Repository](https://github.com/2881099/FreeSql/wiki/Repository)  \|  [UnitOfWork](https://github.com/2881099/FreeSql/wiki/%e5%b7%a5%e4%bd%9c%e5%8d%95%e5%85%83)  \|  [AOP](https://github.com/2881099/FreeSql/wiki/AOP)  \|  [DbContext](https://github.com/2881099/FreeSql/wiki/DbContext)  | +| [Read/Write Separation](https://github.com/2881099/FreeSql/wiki/%e8%af%bb%e5%86%99%e5%88%86%e7%a6%bb)  \|  [Splitting Table/Database](https://github.com/2881099/FreeSql/wiki/%e5%88%86%e8%a1%a8%e5%88%86%e5%ba%93)  \|  [Mysterious Technology](https://github.com/2881099/FreeSql/wiki/%E9%AA%9A%E6%93%8D%E4%BD%9C)  \|  | > FreeSql provides a variety of usage habits, and there is always one that suits you: - Use FreeSql, keep the original usage. -- Use [FreeSql.Repository](https://github.com/2881099/FreeSql/wiki/Repository), for those who are accustomed to storage models and work units. -- Use [FreeSql.DbContext](https://github.com/2881099/FreeSql/wiki/DbContext), a bit like how efcore is used. -- Or use [FreeSql.BaseEntity](https://github.com/2881099/FreeSql/tree/master/Examples/base_entity), which is extremely simple. +- Use [FreeSql.Repository](https://github.com/2881099/FreeSql/wiki/Repository), Repository + UnitOfWork. +- Use [FreeSql.DbContext](https://github.com/2881099/FreeSql/wiki/DbContext), Like efcore. +- Or use [FreeSql.BaseEntity](https://github.com/2881099/FreeSql/tree/master/Examples/base_entity), Simple mode. > Some open source projects that use FreeSql: -- [Zhontai.net Admin, Management System](https://github.com/zhontai/Admin.Core) +- [Zhontai.net Admin Management System](https://github.com/zhontai/Admin.Core) - [A simple and practical CMS implemented by .NET Core](https://github.com/luoyunchong/lin-cms-dotnetcore) -- [iusaas.com SaaS, Enterprise Application Management System](https://github.com/alonsoalon/TenantSite.Server) -- [EasyCms, CMS management system used by enterprises and institutions](https://github.com/jasonyush/EasyCMS) -- [Content management system](https://github.com/hejiyong/fscms)