diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 27909b2e..02eb0609 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -512,14 +512,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerExpression/StringTest.cs b/FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerExpression/StringTest.cs index ad7714d9..48074e69 100644 --- a/FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerExpression/StringTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerExpression/StringTest.cs @@ -96,6 +96,150 @@ namespace FreeSql.Tests.SqlServerExpression public string name { get; set; } } + [Fact] + public void StringJoin02() + { + var fsql = g.sqlserver; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var users = fsql.Insert(new[] + { + new StringJoin02User { UserName = "user01" }, + new StringJoin02User { UserName = "user02" } + }).ExecuteInserted(); + + var roles = fsql.Insert(new[] + { + new StringJoin02Role { RoleName = "role01" }, + new StringJoin02Role { RoleName = "role02" }, + new StringJoin02Role { RoleName = "role03" } + }).ExecuteInserted(); + + fsql.Insert(new[] + { + new StringJoin02UserRole{ UserId = users[0].Id, RoleId = roles[0].Id }, + new StringJoin02UserRole{ UserId = users[0].Id, RoleId = roles[1].Id }, + new StringJoin02UserRole{ UserId = users[0].Id, RoleId = roles[2].Id }, + + new StringJoin02UserRole{ UserId = users[1].Id, RoleId = roles[0].Id }, + new StringJoin02UserRole{ UserId = users[1].Id, RoleId = roles[2].Id }, + }).ExecuteAffrows(); + + var repo = fsql.GetRepository(); + var result = repo.Select.ToList(w => + new + { + w.Id, + w.UserName, + ǰɫ = string.Join(",", repo.Orm + .Select() + .LeftJoin((b, c) => b.RoleId == c.Id) + .Where((b, c) => b.UserId == w.Id) + .ToList((b, c) => c.RoleName)) + }); + Assert.Equal(2, result.Count); + Assert.Equal(users[0].Id, result[0].Id); + Assert.Equal("user01", result[0].UserName); + Assert.Equal("role01,role02,role03", result[0].ǰɫ); + + Assert.Equal(users[1].Id, result[1].Id); + Assert.Equal("user02", result[1].UserName); + Assert.Equal("role01,role03", result[1].ǰɫ); + } + class StringJoin02User + { + [Column(IsIdentity = true)] + public int Id { get; set; } + public string UserName { get; set; } + } + class StringJoin02Role + { + [Column(IsIdentity = true)] + public int Id { get; set; } + public string RoleName { get; set; } + } + class StringJoin02UserRole + { + public int UserId { get; set; } + public int RoleId { get; set; } + } + + [Fact] + public void StringJoin03() + { + var fsql = g.sqlserver; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var users = fsql.Insert(new[] + { + new StringJoin03User { UserName = "user01" }, + new StringJoin03User { UserName = "user02" } + }).ExecuteInserted(); + + var roles = fsql.Insert(new[] + { + new StringJoin03Role { RoleName = "role01" }, + new StringJoin03Role { RoleName = "role02" }, + new StringJoin03Role { RoleName = "role03" } + }).ExecuteInserted(); + + fsql.Insert(new[] + { + new StringJoin03UserRole{ UserId = users[0].Id, RoleId = roles[0].Id }, + new StringJoin03UserRole{ UserId = users[0].Id, RoleId = roles[1].Id }, + new StringJoin03UserRole{ UserId = users[0].Id, RoleId = roles[2].Id }, + + new StringJoin03UserRole{ UserId = users[1].Id, RoleId = roles[0].Id }, + new StringJoin03UserRole{ UserId = users[1].Id, RoleId = roles[2].Id }, + }).ExecuteAffrows(); + + var repo = fsql.GetRepository(); + var result = repo.Select.ToList(w => + new + { + w.Id, + w.UserName, + ǰɫ = string.Join(",", w.Roles.AsSelect().ToList(b => b.RoleName)) + }); + Assert.Equal(2, result.Count); + Assert.Equal(users[0].Id, result[0].Id); + Assert.Equal("user01", result[0].UserName); + Assert.Equal("role01,role02,role03", result[0].ǰɫ); + + Assert.Equal(users[1].Id, result[1].Id); + Assert.Equal("user02", result[1].UserName); + Assert.Equal("role01,role03", result[1].ǰɫ); + } + class StringJoin03User + { + [Column(IsIdentity = true)] + public int Id { get; set; } + public string UserName { get; set; } + + [Navigate(ManyToMany = typeof(StringJoin03UserRole))] + public List Roles { get; set; } + } + class StringJoin03Role + { + [Column(IsIdentity = true)] + public int Id { get; set; } + public string RoleName { get; set; } + + [Navigate(ManyToMany = typeof(StringJoin03UserRole))] + public List Users { get; set; } + } + class StringJoin03UserRole + { + public int UserId { get; set; } + public int RoleId { get; set; } + public StringJoin03User User { get; set; } + public StringJoin03Role Role { get; set; } + } + [Fact] public void First() { diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 7f84d792..d8ca0ba3 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -3133,177 +3133,6 @@ - - - 测试数据库是否连接正确,本方法执行如下命令: - 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> - - - - - - - - 可自定义解析表达式 @@ -4137,12 +3966,6 @@ 超时 - - - 获取资源 - - - 使用完毕后,归还资源 @@ -4213,12 +4036,6 @@ 资源对象 - - - 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 - - 资源对象 - 归还对象给对象池的时候触发 diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 103fefa8..1858e48e 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -700,6 +700,7 @@ namespace FreeSql.Internal static ConcurrentDictionary> _dicMethodExistsExpressionCallAttribute = new ConcurrentDictionary>(); static ConcurrentDictionary _dicTypeExpressionCallClassContextFields = new ConcurrentDictionary(); static ThreadLocal> _subSelectParentDiyMemExps = new ThreadLocal>(); //子查询的所有父自定义查询,比如分组之后的子查询 + static ConcurrentDictionary _dicSelectMethodToSql = new ConcurrentDictionary(); public string ExpressionLambdaToSql(Expression exp, ExpTSC tsc) { if (exp == null) return ""; @@ -1161,16 +1162,16 @@ namespace FreeSql.Internal if (mn == 0) fsqlManyWhereExp = tmpExp; else fsqlManyWhereExp = Expression.AndAlso(fsqlManyWhereExp, tmpExp); } - fsqltables.Add(new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.Parent }); - fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); - var sql2 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { "1" })?.ToString(); - if (string.IsNullOrEmpty(sql2) == false) - manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectWhereSql, Expression.Constant($"exists({sql2.Replace(" \r\n", " \r\n ")})"), Expression.Constant(null)); MethodInfo manySubSelectAggMethod = null; switch (exp3.Method.Name) //https://github.com/dotnetcore/FreeSql/issues/362 { case "Any": case "Count": + fsqltables.Add(new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.Parent }); + fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); + var sql2 = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { "1" })?.ToString(); + if (string.IsNullOrEmpty(sql2) == false) + manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectWhereSql, Expression.Constant($"exists({sql2.Replace(" \r\n", " \r\n ")})"), Expression.Constant(null)); manySubSelectAggMethod = _dicExpressionLambdaToSqlAsSelectAggMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, _ => new ConcurrentDictionary()).GetOrAdd(exp3.Method.Name, exp3MethodName => typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(parm123Ref.RefMiddleEntityType), parm123Ref.RefMiddleEntityType).GetMethod(exp3MethodName, new Type[0])); manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectAggMethod); @@ -1182,7 +1183,28 @@ namespace FreeSql.Internal case "ToList": case "ToOne": case "First": - throw new ArgumentException($"ManyToMany 导航属性 .AsSelect() 暂时不可用于 Sum/Avg/Max/Min/First/ToOne/ToList 方法"); + //解析:string.Join(",", w.Roles.AsSelect().ToList(b => b.RoleName) + var exp3Args0 = (exp3.Arguments[0] as UnaryExpression)?.Operand as LambdaExpression; + manySubSelectAggMethod = _dicSelectMethodToSql.GetOrAdd(fsqlType, fsqlType2 => + fsqlType2.GetMethods().Where(a => a.Name == "ToSql" && a.GetParameters().Length == 2 && a.GetParameters()[1].ParameterType == typeof(FieldAliasOptions) && a.GetGenericArguments().Length == 1).FirstOrDefault()); + if (manySubSelectAggMethod == null || exp3Args0 == null) throw new ArgumentException($"ManyToMany 导航属性 .AsSelect() 暂时不可用于 Sum/Avg/Max/Min/First/ToOne/ToList 方法"); + manySubSelectAggMethod = manySubSelectAggMethod.MakeGenericMethod(exp3Args0.ReturnType); + var fsqls0p = fsql as Select0Provider; + var fsqls0pWhere = fsqls0p._where.ToString(); + fsqls0p._where.Clear(); + var fsqltablesLast = new SelectTableInfo { Alias = manySubSelectWhereParam.Name, Parameter = manySubSelectWhereParam, Table = manyTb, Type = SelectTableInfoType.InnerJoin }; + fsqltables.Add(fsqltablesLast); + fsqlWhere.Invoke(fsql, new object[] { Expression.Lambda(fsqlManyWhereExp, fsqlWhereParam) }); + fsqltablesLast.NavigateCondition = fsqls0p._where.ToString(); + if (fsqltablesLast.NavigateCondition.StartsWith(" AND (")) fsqltablesLast.NavigateCondition = fsqltablesLast.NavigateCondition.Substring(6, fsqltablesLast.NavigateCondition.Length - 7); + fsqls0p._where.Clear().Append(fsqls0pWhere); + var tsc3 = tsc.CloneDisableDiyParse(); + tsc3._tables = tsc._tables.ToList(); + var where2 = ExpressionLambdaToSql(Expression.Lambda(manySubSelectWhereExp, manySubSelectWhereParam), tsc3); + if (string.IsNullOrEmpty(where2) == false) fsqls0p._where.Append(" AND (").Append(where2).Append(")"); + var sql3 = manySubSelectAggMethod.Invoke(fsql, new object[] { exp3Args0, FieldAliasOptions.AsProperty }) as string; + asSelectBefores.Clear(); + return $"({sql3.Replace(" \r\n", " \r\n ")})"; } asSelectBefores.Clear(); return ExpressionLambdaToSql(manySubSelectExpBoy, tsc);