测试通过导航属性 ManyToMany 查询

This commit is contained in:
28810 2019-03-16 22:12:28 +08:00
parent e7be95e6aa
commit e23ae9d7ac
2 changed files with 139 additions and 34 deletions

View File

@ -6,7 +6,7 @@ using System.Linq;
using Xunit; using Xunit;
namespace FreeSql.Tests.MySql { namespace FreeSql.Tests.MySql {
public class MySqlSelectTest { public class MySqlSelectTest {
ISelect<Topic> select => g.mysql.Select<Topic>(); ISelect<Topic> select => g.mysql.Select<Topic>();
@ -79,6 +79,39 @@ namespace FreeSql.Tests.MySql {
} }
[Fact]
public void AsSelect() {
//OneToOne、ManyToOne
var t0 = g.mysql.Select<Tag>().Where(a => a.Parent.Parent.Name == "粤语").ToSql();
//SELECT a.`Id`, a.`Parent_id`, a__Parent.`Id` as3, a__Parent.`Parent_id` as4, a__Parent.`Ddd`, a__Parent.`Name`, a.`Ddd` as7, a.`Name` as8
//FROM `Tag` a
//LEFT JOIN `Tag` a__Parent ON a__Parent.`Id` = a.`Parent_id`
//LEFT JOIN `Tag` a__Parent__Parent ON a__Parent__Parent.`Id` = a__Parent.`Parent_id`
//WHERE (a__Parent__Parent.`Name` = '粤语')
//OneToMany
var t1 = g.mysql.Select<Tag>().Where(a => a.Tags.AsSelect().Any(t => t.Parent.Id == 10)).ToSql();
//SELECT a.`Id`, a.`Parent_id`, a.`Ddd`, a.`Name`
//FROM `Tag` a
//WHERE (exists(SELECT 1
// FROM `Tag` t
// LEFT JOIN `Tag` t__Parent ON t__Parent.`Id` = t.`Parent_id`
// WHERE (t__Parent.`Id` = 10) AND (t.`Parent_id` = a.`Id`)
// limit 0,1))
//ManyToMany
var t2 = g.mysql.Select<Song>().Where(s => s.Tags.AsSelect().Any(t => t.Name == "国语")).ToSql();
//SELECT a.`Id`, a.`Create_time`, a.`Is_deleted`, a.`Title`, a.`Url`
//FROM `Song` a
//WHERE(exists(SELECT 1
// FROM `Song_tag` Mt_Ms
// WHERE(Mt_Ms.`Song_id` = a.`Id`) AND(exists(SELECT 1
// FROM `Tag` t
// WHERE(t.`Name` = '国语') AND(t.`Id` = Mt_Ms.`Tag_id`)
// limit 0, 1))
// limit 0, 1))
}
[Fact] [Fact]
public void Lazy() { public void Lazy() {
var tags = g.mysql.Select<Tag>().Where(a => a.Parent.Name == "xxx") var tags = g.mysql.Select<Tag>().Where(a => a.Parent.Name == "xxx")
@ -140,22 +173,22 @@ namespace FreeSql.Tests.MySql {
.Where(a => b.Name != "xxx")); .Where(a => b.Name != "xxx"));
var list111sql = list111.ToSql(); var list111sql = list111.ToSql();
var list111data = list111.ToList((a, b, c) => new { var list111data = list111.ToList((a, b, c) => new {
a.Id,
title_substring = a.Title.Substring(0, 1),
a.Type,
ccc = new { a.Id, a.Title },
tp = a.Type,
tp2 = new {
a.Id, a.Id,
title_substring = a.Title.Substring(0, 1), tp2 = a.Type.Name
a.Type, },
ccc = new { a.Id, a.Title }, tp3 = new {
tp = a.Type, a.Id,
tp2 = new { tp33 = new {
a.Id, a.Id
tp2 = a.Type.Name
},
tp3 = new {
a.Id,
tp33 = new {
a.Id
}
} }
}); }
});
var ttt122 = g.mysql.Select<TestTypeParentInfo>().Where(a => a.Id > 0).ToSql(); var ttt122 = g.mysql.Select<TestTypeParentInfo>().Where(a => a.Id > 0).ToSql();
var sql5 = g.mysql.Select<TestInfo>().From<TestTypeInfo, TestTypeParentInfo>((s, b, c) => s).Where((a, b, c) => a.Id == b.ParentId).ToSql(); var sql5 = g.mysql.Select<TestInfo>().From<TestTypeInfo, TestTypeParentInfo>((s, b, c) => s).Where((a, b, c) => a.Id == b.ParentId).ToSql();
@ -491,7 +524,7 @@ namespace FreeSql.Tests.MySql {
sql = query.ToSql().Replace("\r\n", ""); sql = query.ToSql().Replace("\r\n", "");
Assert.Equal("SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime` FROM `tb_topic` a, `TestTypeInfo` a__Type, `TestTypeParentInfo` a__Type__Parent WHERE (a__Type__Parent.`Name` = 'tparent')", sql); Assert.Equal("SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime` FROM `tb_topic` a, `TestTypeInfo` a__Type, `TestTypeParentInfo` a__Type__Parent WHERE (a__Type__Parent.`Name` = 'tparent')", sql);
query.ToList(); query.ToList();
//<2F><><EFBFBD>û<EFBFBD>е<EFBFBD><D0B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԣ<EFBFBD><D4A3>򵥶<EFBFBD><F2B5A5B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD> //<2F><><EFBFBD>û<EFBFBD>е<EFBFBD><D0B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԣ<EFBFBD><D4A3>򵥶<EFBFBD><F2B5A5B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
query = select.Where<TestTypeInfo>((a, b) => b.Guid == a.TypeGuid && b.Name == "typeTitle"); query = select.Where<TestTypeInfo>((a, b) => b.Guid == a.TypeGuid && b.Name == "typeTitle");
sql = query.ToSql().Replace("\r\n", ""); sql = query.ToSql().Replace("\r\n", "");

View File

@ -205,7 +205,10 @@ namespace FreeSql.Internal {
} }
static ConcurrentDictionary<Type, MethodInfo> _dicExpressionLambdaToSqlAsSelectMethodInfo = new ConcurrentDictionary<Type, MethodInfo>(); static ConcurrentDictionary<Type, MethodInfo> _dicExpressionLambdaToSqlAsSelectMethodInfo = new ConcurrentDictionary<Type, MethodInfo>();
static ConcurrentDictionary<Type, MethodInfo> _dicExpressionLambdaToSqlAsSelectWhereMethodInfo = new ConcurrentDictionary<Type, MethodInfo>(); static ConcurrentDictionary<Type, MethodInfo> _dicExpressionLambdaToSqlAsSelectWhereMethodInfo = new ConcurrentDictionary<Type, MethodInfo>();
static ConcurrentDictionary<Type, MethodInfo> _dicExpressionLambdaToSqlAsSelectWhereSqlMethodInfo = new ConcurrentDictionary<Type, MethodInfo>();
static ConcurrentDictionary<Type, MethodInfo> _dicExpressionLambdaToSqlAsSelectAnyMethodInfo = new ConcurrentDictionary<Type, MethodInfo>();
static ConcurrentDictionary<Type, PropertyInfo> _dicNullableValueProperty = new ConcurrentDictionary<Type, PropertyInfo>(); static ConcurrentDictionary<Type, PropertyInfo> _dicNullableValueProperty = new ConcurrentDictionary<Type, PropertyInfo>();
static ConcurrentDictionary<Type, Expression> _dicFreeSqlGlobalExtensionsAsSelectExpression = new ConcurrentDictionary<Type, Expression>();
internal string ExpressionLambdaToSql(Expression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { internal string ExpressionLambdaToSql(Expression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
if (exp == null) return ""; if (exp == null) return "";
switch (exp.NodeType) { switch (exp.NodeType) {
@ -280,13 +283,14 @@ namespace FreeSql.Internal {
if (asSelectBefores.Any()) { if (asSelectBefores.Any()) {
asSelectParentExp1 = asSelectBefores.Pop() as MemberExpression; asSelectParentExp1 = asSelectBefores.Pop() as MemberExpression;
asSelectParentExp = asSelectBefores.Pop(); if (asSelectBefores.Any()) {
if (asSelectParentExp != null) { asSelectParentExp = asSelectBefores.Pop();
var testExecuteExp = asSelectParentExp; if (asSelectParentExp != null) {
if (asSelectParentExp.NodeType == ExpressionType.Parameter) //执行leftjoin关联 var testExecuteExp = asSelectParentExp;
testExecuteExp = Expression.Property(testExecuteExp, _common.GetTableByEntity(asSelectParentExp.Type).Properties.First().Value); if (asSelectParentExp.NodeType == ExpressionType.Parameter) //执行leftjoin关联
asSelectSql = ExpressionLambdaToSql(testExecuteExp, _tables, new List<SelectColumnInfo>(), getSelectGroupingMapString, SelectTableInfoType.LeftJoin, isQuoteName); testExecuteExp = Expression.Property(testExecuteExp, _common.GetTableByEntity(asSelectParentExp.Type).Properties.First().Value);
asSelectSql = ExpressionLambdaToSql(testExecuteExp, _tables, new List<SelectColumnInfo>(), getSelectGroupingMapString, SelectTableInfoType.LeftJoin, isQuoteName);
}
} }
} }
} }
@ -313,22 +317,27 @@ namespace FreeSql.Internal {
var parms = method.GetParameters(); var parms = method.GetParameters();
var args = new object[call3Exp.Arguments.Count]; var args = new object[call3Exp.Arguments.Count];
for (var a = 0; a < args.Length; a++) { for (var a = 0; a < args.Length; a++) {
var argExp = (call3Exp.Arguments[a] as UnaryExpression)?.Operand; var arg3Exp = call3Exp.Arguments[a];
if (argExp != null && argExp.NodeType == ExpressionType.Lambda) { if (arg3Exp.NodeType == ExpressionType.Constant) {
if (fsqltable1SetAlias == false) { args[a] = (arg3Exp as ConstantExpression)?.Value;
fsqltables[0].Alias = (argExp as LambdaExpression).Parameters.First().Name; } else {
fsqltable1SetAlias = true; var argExp = (arg3Exp as UnaryExpression)?.Operand;
if (argExp != null && argExp.NodeType == ExpressionType.Lambda) {
if (fsqltable1SetAlias == false) {
fsqltables[0].Alias = (argExp as LambdaExpression).Parameters.First().Name;
fsqltable1SetAlias = true;
}
} }
args[a] = argExp;
//if (args[a] == null) ExpressionLambdaToSql(call3Exp.Arguments[a], fsqltables, null, null, SelectTableInfoType.From, true);
} }
args[a] = argExp;
//if (args[a] == null) ExpressionLambdaToSql(call3Exp.Arguments[a], fsqltables, null, null, SelectTableInfoType.From, true);
} }
method.Invoke(fsql, args); method.Invoke(fsql, args);
} }
if (fsql == null) asSelectBefores.Push(exp3tmp); if (fsql == null) asSelectBefores.Push(exp3tmp);
} }
if (fsql != null) { if (fsql != null) {
if (asSelectEntityType != null) { //执行 asSelect() 的关联 if (asSelectParentExp != null) { //执行 asSelect() 的关联OneToManyManyToMany
var fsqlWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(asSelectEntityType, asSelectEntityType3 => var fsqlWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(asSelectEntityType, asSelectEntityType3 =>
typeof(ISelect<>).MakeGenericType(asSelectEntityType3).GetMethod("Where", new[] { typeof(ISelect<>).MakeGenericType(asSelectEntityType3).GetMethod("Where", new[] {
typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(asSelectEntityType3, typeof(bool))) typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(asSelectEntityType3, typeof(bool)))
@ -337,6 +346,70 @@ namespace FreeSql.Internal {
var parm123Ref = parm123Tb.GetTableRef(asSelectParentExp1.Member.Name); var parm123Ref = parm123Tb.GetTableRef(asSelectParentExp1.Member.Name);
var fsqlWhereParam = fsqltables.First().Parameter; //Expression.Parameter(asSelectEntityType); var fsqlWhereParam = fsqltables.First().Parameter; //Expression.Parameter(asSelectEntityType);
Expression fsqlWhereExp = null; Expression fsqlWhereExp = null;
if (parm123Ref.RefType == TableRefType.ManyToMany) {
//g.mysql.Select<Tag>().Where(a => g.mysql.Select<Song_tag>().Where(b => b.Tag_id == a.Id && b.Song_id == 1).Any());
var manyTb = _common.GetTableByEntity(parm123Ref.RefMiddleEntityType);
var manySubSelectWhere = _dicExpressionLambdaToSqlAsSelectWhereMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 =>
typeof(ISelect<>).MakeGenericType(refMiddleEntityType3).GetMethod("Where", new[] {
typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(refMiddleEntityType3, typeof(bool)))
}));
var manySubSelectWhereSql = _dicExpressionLambdaToSqlAsSelectWhereSqlMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 =>
typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(refMiddleEntityType3), refMiddleEntityType3).GetMethod("Where", new[] { typeof(string), typeof(object) }));
var manySubSelectAny = _dicExpressionLambdaToSqlAsSelectAnyMethodInfo.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 =>
typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(refMiddleEntityType3), refMiddleEntityType3).GetMethod("Any", new Type[0]));
var manySubSelectAsSelectExp = _dicFreeSqlGlobalExtensionsAsSelectExpression.GetOrAdd(parm123Ref.RefMiddleEntityType, refMiddleEntityType3 =>
Expression.Call(
typeof(FreeSqlGlobalExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(mfil => mfil.Name == "AsSelect" && mfil.GetParameters().Length == 1).FirstOrDefault()?.MakeGenericMethod(refMiddleEntityType3),
Expression.Constant(Activator.CreateInstance(typeof(List<>).MakeGenericType(refMiddleEntityType3)))
));
var manyMainParam = _tables[0].Parameter;
var manySubSelectWhereParam = Expression.Parameter(parm123Ref.RefMiddleEntityType, $"M{fsqlWhereParam.Name}_M{asSelectParentExp.ToString().Replace(".", "__")}");//, $"{fsqlWhereParam.Name}__");
Expression manySubSelectWhereExp = null;
for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) {
var col1 = parm123Ref.MiddleColumns[mn];
var col2 = parm123Ref.Columns[mn];
var pexp1 = Expression.Property(manySubSelectWhereParam, col1.CsName);
var pexp2 = Expression.Property(asSelectParentExp, col2.CsName);
if (col1.CsType != col2.CsType) {
if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value")));
if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value")));
}
var tmpExp = Expression.Equal(pexp1, pexp2);
if (mn == 0) manySubSelectWhereExp = tmpExp;
else manySubSelectWhereExp = Expression.And(manySubSelectWhereExp, tmpExp);
}
var manySubSelectExpBoy = Expression.Call(
manySubSelectAsSelectExp,
manySubSelectWhere,
Expression.Lambda(
manySubSelectWhereExp,
manySubSelectWhereParam
)
);
Expression fsqlManyWhereExp = null;
for (var mn = 0; mn < parm123Ref.RefColumns.Count; mn++) {
var col1 = parm123Ref.RefColumns[mn];
var col2 = parm123Ref.MiddleColumns[mn + parm123Ref.Columns.Count + mn];
var pexp1 = Expression.Property(fsqlWhereParam, col1.CsName);
var pexp2 = Expression.Property(manySubSelectWhereParam, col2.CsName);
if (col1.CsType != col2.CsType) {
if (col1.CsType.IsNullableType()) pexp1 = Expression.Property(pexp1, _dicNullableValueProperty.GetOrAdd(col1.CsType, ct1 => ct1.GetProperty("Value")));
if (col2.CsType.IsNullableType()) pexp2 = Expression.Property(pexp2, _dicNullableValueProperty.GetOrAdd(col2.CsType, ct2 => ct2.GetProperty("Value")));
}
var tmpExp = Expression.Equal(pexp1, pexp2);
if (mn == 0) fsqlManyWhereExp = tmpExp;
else fsqlManyWhereExp = Expression.And(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\t")})"), Expression.Constant(null));
manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectAny);
asSelectBefores.Clear();
return ExpressionLambdaToSql(manySubSelectExpBoy, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
}
for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) { for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) {
var col1 = parm123Ref.RefColumns[mn]; var col1 = parm123Ref.RefColumns[mn];
var col2 = parm123Ref.Columns[mn]; var col2 = parm123Ref.Columns[mn];
@ -354,9 +427,8 @@ namespace FreeSql.Internal {
} }
asSelectBefores.Clear(); asSelectBefores.Clear();
var sql = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { "1" })?.ToString(); var sql = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { "1" })?.ToString();
if (string.IsNullOrEmpty(sql) == false) { if (string.IsNullOrEmpty(sql) == false)
return $"exists({sql.Replace("\r\n", "\r\n\t")})"; return $"exists({sql.Replace("\r\n", "\r\n\t")})";
}
} }
asSelectBefores.Clear(); asSelectBefores.Clear();
} }
@ -471,7 +543,7 @@ namespace FreeSql.Internal {
find.Parameter = parmExp; find.Parameter = parmExp;
if (find == null) { if (find == null) {
_tables.Add(find = new SelectTableInfo { Table = tbtmp, Alias = alias, On = null, Type = mp == null ? tbtype : SelectTableInfoType.LeftJoin, Parameter = isa ? parmExp : null }); _tables.Add(find = new SelectTableInfo { Table = tbtmp, Alias = alias, On = null, Type = mp == null ? tbtype : SelectTableInfoType.LeftJoin, Parameter = isa ? parmExp : null });
if (mp?.Expression != null) { //导航条件 if (mp?.Expression != null) { //导航条件OneToOne、ManyToOne
var firstTb = _tables.First().Table; var firstTb = _tables.First().Table;
var parentTb = _common.GetTableByEntity(mp.Expression.Type); var parentTb = _common.GetTableByEntity(mp.Expression.Type);
var parentTbRef = parentTb.GetTableRef(mp.Member.Name); var parentTbRef = parentTb.GetTableRef(mp.Member.Name);