diff --git a/Examples/base_entity/Program.cs b/Examples/base_entity/Program.cs index 5072cf2e..5c1f9083 100644 --- a/Examples/base_entity/Program.cs +++ b/Examples/base_entity/Program.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json.Linq; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Data.Common; using System.Data.Odbc; using System.Data.SqlClient; using System.Data.SQLite; @@ -388,6 +389,45 @@ namespace base_entity BaseEntity.Initialization(fsql, () => _asyncUow.Value); #endregion + var dbpars = new List(); + + var a1id1 = Guid.NewGuid(); + var a1id2 = Guid.NewGuid(); + //fsql.CodeFirst.IsGenerateCommandParameterWithLambda = true; + var sql1a0 = fsql.Select() + .WithParameters(dbpars) + .Where(a => a.Id == a1id1) + + .UnionAll( + fsql.Select() + .WithParameters(dbpars) + .Where(a => a.Id == a1id2) + ) + .Where(a => a.Id == a1id1 || a.Id == a1id2) + .ToSql(); + var sql1a1 = fsql.Select() + .Where(a => a.Id == a1id1) + .UnionAll( + fsql.Select() + .Where(a => a.Id == a1id2) + ) + .Where(a => a.Id == a1id1 || a.Id == a1id2) + .ToSql(); + var sql1a2 = fsql.Select() + .InnerJoin((a,b)=> a.GroupId == b.Id) + .Where((a, b) => a.Id == a1id1) + .WithTempQuery((a, b) => new { user = a, group = b }) //匿名类型 + + .UnionAll( + fsql.Select() + .InnerJoin((a, b) => a.GroupId == b.Id) + .Where((a, b) => a.Id == a1id2) + .WithTempQuery((a, b) => new { user = a, group = b }) //匿名类型 + ) + + .Where(a => a.user.Id == a1id1 || a.user.Id == a1id2) + .ToSql(); + var ddlsql01 = fsql.CodeFirst.GetComparisonDDLStatements(); diff --git a/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerSelectWithTempQueryTest.cs b/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerSelectWithTempQueryTest.cs index 61daae08..e10f938e 100644 --- a/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerSelectWithTempQueryTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerSelectWithTempQueryTest.cs @@ -434,6 +434,77 @@ WHERE (a.[rownum] = 1)"; Assert.Equal(list01[2].item.Nickname, "name03"); + var sql0111 = fsql.Select() + .WithTempQuery(a => new + { + item = a, + rownum = SqlExt.RowNumber().Over().PartitionBy(a.Nickname).OrderBy(a.Id).ToValue() + }) + .Where(a => a.rownum == 1) + .UnionAll( + fsql.Select() + .WithTempQuery(a => new + { + item = a, + rownum = SqlExt.RowNumber().Over().PartitionBy(a.Nickname).OrderByDescending(a.Id).ToValue() + }) + .Where(a => a.rownum == 2) + ) + .Where(a => a.rownum == 1 || a.rownum == 2) + .ToSql(); + var assertSql0111 = @"SELECT * +FROM ( SELECT * + FROM ( + SELECT a.[Id], a.[Nickname], row_number() over( partition by a.[Nickname] order by a.[Id]) [rownum] + FROM [SingleTablePartitionBy_User] a ) a + WHERE (a.[rownum] = 1) + UNION ALL + SELECT * + FROM ( + SELECT a.[Id], a.[Nickname], row_number() over( partition by a.[Nickname] order by a.[Id] desc) [rownum] + FROM [SingleTablePartitionBy_User] a ) a + WHERE (a.[rownum] = 2) ) a +WHERE ((a.[rownum] = 1 OR a.[rownum] = 2))"; + Assert.Equal(assertSql0111, sql0111); + + var sel0111 = fsql.Select() + .WithTempQuery(a => new + { + item = a, + rownum = SqlExt.RowNumber().Over().PartitionBy(a.Nickname).OrderBy(a.Id).ToValue() + }) + .Where(a => a.rownum == 1) + .UnionAll( + fsql.Select() + .WithTempQuery(a => new + { + item = a, + rownum = SqlExt.RowNumber().Over().PartitionBy(a.Nickname).OrderByDescending(a.Id).ToValue() + }) + .Where(a => a.rownum == 2) + ) + .Where(a => a.rownum == 1 || a.rownum == 2); + Assert.Equal(assertSql0111, sel0111.ToSql()); + + var list0111 = sel0111.ToList(); + Assert.Equal(5, list0111.Count); + Assert.Equal(list0111[0].rownum, 1); + Assert.Equal(list0111[0].item.Id, 1); + Assert.Equal(list0111[0].item.Nickname, "name01"); + Assert.Equal(list0111[1].rownum, 1); + Assert.Equal(list0111[1].item.Id, 4); + Assert.Equal(list0111[1].item.Nickname, "name02"); + Assert.Equal(list0111[2].rownum, 1); + Assert.Equal(list0111[2].item.Id, 5); + Assert.Equal(list0111[2].item.Nickname, "name03"); + Assert.Equal(list0111[3].rownum, 2); + Assert.Equal(list0111[3].item.Id, 2); + Assert.Equal(list0111[3].item.Nickname, "name01"); + Assert.Equal(list0111[4].rownum, 2); + Assert.Equal(list0111[4].item.Id, 5); + Assert.Equal(list0111[4].item.Nickname, "name03"); + + var sql011 = fsql.Select() .GroupBy(a => a.Nickname) .WithTempQuery(g => new { min = g.Min(g.Value.Id) }) diff --git a/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs b/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs index a4405d35..3b525e8c 100644 --- a/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs +++ b/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs @@ -263,7 +263,7 @@ namespace FreeSql.Tests var ddlsql = g.sqlite.CodeFirst.GetComparisonDDLStatements(typeof(testInsertNullable), "tb123123"); Assert.Equal(@"CREATE TABLE IF NOT EXISTS ""main"".""tb123123"" ( - ""Id"" INTEGER PRIMARY KEY AUTOINCREMENT, + ""Id"" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ""str1"" NVARCHAR(255) NOT NULL, ""int1"" INTEGER NOT NULL, ""int2"" INTEGER , diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 109dc1ea..74b1a582 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1890,6 +1890,13 @@ + + + 使用自定义参数化,UnionALL 或者 ToSql 可能有需要 + + + + 命令超时设置(秒) @@ -3342,6 +3349,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> + + + + + + + + 可自定义解析表达式 @@ -4372,6 +4550,12 @@ 超时 + + + 获取资源 + + + 使用完毕后,归还资源 @@ -4447,6 +4631,12 @@ 资源对象 + + + 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 + + 资源对象 + 归还对象给对象池的时候触发 @@ -5888,222 +6078,3 @@ -ystem.Boolean}})"> - - 使用 and 拼接两个 lambda 表达式 - - - true 时生效 - - - - - - 使用 or 拼接两个 lambda 表达式 - - - - - - 使用 or 拼接两个 lambda 表达式 - - - true 时生效 - - - - - - 将 lambda 表达式取反 - - - true 时生效 - - - - - 使用 and 拼接两个 lambda 表达式 - - - - - - 使用 and 拼接两个 lambda 表达式 - - - true 时生效 - - - - - - 使用 or 拼接两个 lambda 表达式 - - - - - - 使用 or 拼接两个 lambda 表达式 - - - true 时生效 - - - - - - 将 lambda 表达式取反 - - - true 时生效 - - - - - 生成类似Mongodb的ObjectId有序、不重复Guid - - - - - - 插入数据 - - - - - - - 插入数据,传入实体 - - - - - - - - 插入数据,传入实体数组 - - - - - - - - 插入数据,传入实体集合 - - - - - - - - 插入数据,传入实体集合 - - - - - - - - 插入或更新数据,此功能依赖数据库特性(低版本可能不支持),参考如下: - MySql 5.6+: on duplicate key update - PostgreSQL 9.4+: on conflict do update - SqlServer 2008+: merge into - Oracle 11+: merge into - Sqlite: replace into - 达梦: merge into - 人大金仓:on conflict do update - 神通:merge into - MsAccess:不支持 - 注意区别:FreeSql.Repository 仓储也有 InsertOrUpdate 方法(不依赖数据库特性) - - - - - - - 修改数据 - - - - - - - 修改数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 查询数据 - - - - - - - 查询数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 删除数据 - - - - - - - 删除数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 开启事务(不支持异步) - v1.5.0 关闭了线程事务超时自动提交的机制 - - 事务体 () => {} - - - - 开启事务(不支持异步) - v1.5.0 关闭了线程事务超时自动提交的机制 - - - 事务体 () => {} - - - - 数据库访问对象 - - - - - 所有拦截方法都在这里 - - - - - CodeFirst 模式开发相关方法 - - - - - DbFirst 模式开发相关方法 - - - - - 全局过滤设置,可默认附加为 Select/Update/Delete 条件 - - - - diff --git a/FreeSql/Interface/Curd/ISelect/ISelect0.cs b/FreeSql/Interface/Curd/ISelect/ISelect0.cs index b845bf3c..989cd188 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect0.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect0.cs @@ -56,6 +56,12 @@ namespace FreeSql /// TSelect WithConnection(DbConnection connection); /// + /// 使用自定义参数化,UnionALL 或者 ToSql 可能有需要 + /// + /// + /// + TSelect WithParameters(List parameters); + /// /// 命令超时设置(秒) /// /// diff --git a/FreeSql/Interface/Curd/ISelect/ISelect1.cs b/FreeSql/Interface/Curd/ISelect/ISelect1.cs index ae032f97..a0398892 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect1.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect1.cs @@ -193,6 +193,7 @@ namespace FreeSql ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ISelectFromExpression>> exp = null) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class where T16 : class; ISelect FromQuery(ISelect select2) where T2 : class; + ISelect UnionAll(ISelect select2); /// /// 查询条件,Where(a => a.Id > 10),支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com") diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 325222d1..308b577f 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -541,6 +541,11 @@ namespace FreeSql.Internal.CommonProvider _connection = connection; return this as TSelect; } + public TSelect WithParameters(List parameters) + { + if (parameters != null) _params = parameters; + return this as TSelect; + } public TSelect CommandTimeout(int timeout) { _commandTimeout = timeout; @@ -802,7 +807,7 @@ namespace FreeSql.Internal.CommonProvider var unions = new List>(); var trs = _tableRules.Any() ? _tableRules : new List>(new[] { new Func((type, oldname) => null) }); - if (trs.Count == 1 && _tables.Any(a => a.Table.AsTableImpl != null && string.IsNullOrWhiteSpace(trs[0](a.Table.Type, a.Table.DbName)) == true)) + if (trs.Count == 1 && _tables.Any(a => a.Table != null && a.Table.AsTableImpl != null && string.IsNullOrWhiteSpace(trs[0](a.Table.Type, a.Table.DbName)) == true)) { string[] LocalGetTableNames(SelectTableInfo tb) { @@ -1196,6 +1201,11 @@ namespace FreeSql.Internal.CommonProvider if (_orm.CodeFirst.IsAutoSyncStructure) (_orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TDto)); //._dicSyced.TryAdd(typeof(TReturn), true); var ret = (_orm as BaseDbProvider).CreateSelectProvider(null) as Select1Provider; + ret._commandTimeout = _commandTimeout; + ret._connection = _connection; + ret._transaction = _transaction; + ret._whereGlobalFilter = new List(_whereGlobalFilter.ToArray()); + ret._cancel = _cancel; ret._params.AddRange(_params); if (ret._tables[0].Table == null) ret._tables[0].Table = TableInfo.GetDefaultTable(typeof(TDto)); var parser = new WithTempQueryParser(this, null, selector, ret._tables[0]); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 5ab05599..f273009b 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -172,6 +172,22 @@ namespace FreeSql.Internal.CommonProvider if (retsp._tableRules.Count == 0) ret.WithSql(null, $" \r\n{sql2}"); return ret; } + public ISelect UnionAll(ISelect select2) + { + var sql1 = this.ToSql(); + var sql2 = select2.ToSql(); + var ret = (_orm as BaseDbProvider).CreateSelectProvider(null) as Select1Provider; + ret.WithSql($"{sql1} \r\nUNION ALL \r\n{sql2}"); + ret._commandTimeout = _commandTimeout; + ret._connection = _connection; + ret._transaction = _transaction; + ret._whereGlobalFilter = new List(_whereGlobalFilter.ToArray()); + ret._cancel = _cancel; + ret._diymemexpWithTempQuery = _diymemexpWithTempQuery; + ret._tables[0] = _tables[0]; + ret._params = _params; + return ret; + } public ISelectGrouping GroupBy(Expression> columns) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs index aa2cc7bb..6881b2e5 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs @@ -234,6 +234,11 @@ namespace FreeSql.Internal.CommonProvider if (_orm.CodeFirst.IsAutoSyncStructure) (_orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TDto)); //._dicSyced.TryAdd(typeof(TReturn), true); var ret = (_orm as BaseDbProvider).CreateSelectProvider(null) as Select1Provider; + ret._commandTimeout = _select._commandTimeout; + ret._connection = _select._connection; + ret._transaction = _select._transaction; + ret._whereGlobalFilter = new List(_select._whereGlobalFilter.ToArray()); + ret._cancel = _select._cancel; if (ret._tables[0].Table == null) ret._tables[0].Table = TableInfo.GetDefaultTable(typeof(TDto)); var parser = new Select0Provider.WithTempQueryParser(_select, this, selector, ret._tables[0]); var sql = $"\r\n{this.ToSql(parser._insideSelectList[0].InsideField)}";