自动贪婪加载 LeftJoin/InnerJoin/RightJoin 数据

This commit is contained in:
2881099 2019-04-20 01:21:57 +08:00
parent 6c64eac419
commit 191a15fb42
3 changed files with 141 additions and 14 deletions

View File

@ -10,17 +10,17 @@ namespace FreeSql.Tests.Sqlite {
public class SqliteCodeFirstTest { public class SqliteCodeFirstTest {
class Topic { public class Topic {
public Guid Id { get; set; } public Guid Id { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string Content { get; set; } public string Content { get; set; }
public DateTime CreateTime { get; set; } public DateTime CreateTime { get; set; }
} }
[Table(Name = "xxxtb.Comment")] [Table(Name = "xxxtb.Comment")]
class Comment { public class Comment {
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid TopicId { get; set; } public Guid TopicId { get; set; }
public Topic Topic { get; set; } public virtual Topic Topic { get; set; }
public string Nickname { get; set; } public string Nickname { get; set; }
public string Content { get; set; } public string Content { get; set; }
public DateTime CreateTime { get; set; } public DateTime CreateTime { get; set; }

View File

@ -54,6 +54,10 @@ namespace FreeSql.Tests {
[Fact] [Fact]
public void Test1() { public void Test1() {
var sql = g.sqlite.Select<TestTypeParentInfo>().Where(a => a.Parent.Parent.Parent.Name == "11").ToSql();
var sql222 = g.sqlite.Select<TestTypeParentInfo>().Where(a => a.Parent.Parent.Parent.Name == "11").ToList();
Expression<Func<TestInfo, object>> orderBy = null; Expression<Func<TestInfo, object>> orderBy = null;
orderBy = a => a.CreateTime; orderBy = a => a.CreateTime;
var testsql1 = select.OrderBy(orderBy).ToSql(); var testsql1 = select.OrderBy(orderBy).ToSql();

View File

@ -46,7 +46,7 @@ namespace FreeSql.Internal.CommonProvider {
//toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List<SelectTableInfo>(from._tables.ToArray())); //toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List<SelectTableInfo>(from._tables.ToArray()));
var _multiTables = toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(to) as List<SelectTableInfo>; var _multiTables = toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(to) as List<SelectTableInfo>;
_multiTables[0] = from._tables[0]; _multiTables[0] = from._tables[0];
for (var a = 1;a < lambParms.Count; a++) { for (var a = 1; a < lambParms.Count; a++) {
var tb = from._tables.Where(b => b.Alias == lambParms[a].Name && b.Table.Type == lambParms[a].Type).FirstOrDefault(); var tb = from._tables.Where(b => b.Alias == lambParms[a].Name && b.Table.Type == lambParms[a].Type).FirstOrDefault();
if (tb != null) _multiTables[a] = tb; if (tb != null) _multiTables[a] = tb;
else { else {
@ -235,7 +235,7 @@ namespace FreeSql.Internal.CommonProvider {
Type type = typeof(TTuple); Type type = typeof(TTuple);
_orm.Ado.ExecuteReader(_connection, _transaction, dr => { _orm.Ado.ExecuteReader(_connection, _transaction, dr => {
var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr, 0, _commonUtils); var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr, 0, _commonUtils);
ret.Add((TTuple)read.Value); ret.Add((TTuple) read.Value);
}, CommandType.Text, sql, _params.ToArray()); }, CommandType.Text, sql, _params.ToArray());
_orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret)); _orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret));
_trackToList?.Invoke(ret); _trackToList?.Invoke(ret);
@ -251,7 +251,7 @@ namespace FreeSql.Internal.CommonProvider {
Type type = typeof(TTuple); Type type = typeof(TTuple);
await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => {
var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr, 0, _commonUtils); var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr, 0, _commonUtils);
ret.Add((TTuple)read.Value); ret.Add((TTuple) read.Value);
return Task.CompletedTask; return Task.CompletedTask;
}, CommandType.Text, sql, _params.ToArray()); }, CommandType.Text, sql, _params.ToArray());
_orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret)); _orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret));
@ -311,7 +311,7 @@ namespace FreeSql.Internal.CommonProvider {
Type type = typeof(TReturn); Type type = typeof(TReturn);
_orm.Ado.ExecuteReader(_connection, _transaction, dr => { _orm.Ado.ExecuteReader(_connection, _transaction, dr => {
var index = -1; var index = -1;
ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index, false)); ret.Add((TReturn) _commonExpression.ReadAnonymous(af.map, dr, ref index, false));
}, CommandType.Text, sql, _params.ToArray()); }, CommandType.Text, sql, _params.ToArray());
_orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret)); _orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret));
_trackToList?.Invoke(ret); _trackToList?.Invoke(ret);
@ -327,7 +327,7 @@ namespace FreeSql.Internal.CommonProvider {
Type type = typeof(TReturn); Type type = typeof(TReturn);
await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => {
var index = -1; var index = -1;
ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index, false)); ret.Add((TReturn) _commonExpression.ReadAnonymous(af.map, dr, ref index, false));
return Task.CompletedTask; return Task.CompletedTask;
}, CommandType.Text, sql, _params.ToArray()); }, CommandType.Text, sql, _params.ToArray());
_orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret)); _orm.Aop.ToList?.Invoke(this, new AopToListEventArgs(ret));
@ -350,6 +350,126 @@ namespace FreeSql.Internal.CommonProvider {
public Func<IFreeSql, DbDataReader, T1> Read { get; set; } public Func<IFreeSql, DbDataReader, T1> Read { get; set; }
} }
protected GetAllFieldExpressionTreeInfo GetAllFieldExpressionTree() { protected GetAllFieldExpressionTreeInfo GetAllFieldExpressionTree() {
return _dicGetAllFieldExpressionTree.GetOrAdd(string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Alias}-{a.Type}")), s => {
var type = _tables.First().Table.TypeLazy ?? _tables.First().Table.Type;
var ormExp = Expression.Parameter(typeof(IFreeSql), "orm");
var rowExp = Expression.Parameter(typeof(DbDataReader), "row");
var returnTarget = Expression.Label(type);
var retExp = Expression.Variable(type, "ret");
var dataIndexExp = Expression.Variable(typeof(int), "dataIndex");
var readExp = Expression.Variable(typeof(Utils.RowInfo), "read");
var readExpValue = Expression.MakeMemberAccess(readExp, Utils.RowInfo.PropertyValue);
var readExpDataIndex = Expression.MakeMemberAccess(readExp, Utils.RowInfo.PropertyDataIndex);
var blockExp = new List<Expression>();
var ctor = type.GetConstructor(new Type[0]) ?? type.GetConstructors().First();
blockExp.AddRange(new Expression[] {
Expression.Assign(retExp, Expression.New(ctor, ctor.GetParameters().Select(a => Expression.Default(a.ParameterType)))),
Expression.Assign(dataIndexExp, Expression.Constant(0))
});
//typeof(Topic).GetMethod("get_Type").IsVirtual
var field = new StringBuilder();
var dicfield = new Dictionary<string, bool>();
var tb = _tables.First();
var index = 0;
var tbiindex = 0;
foreach (var tbi in _tables.ToArray().OrderBy(a => a.Alias)) {
if (tbiindex > 0 && tbi.Type == SelectTableInfoType.From) continue;
if (tbiindex > 0 && tbi.Alias.StartsWith($"{tb.Alias}__") == false) continue;
var typei = tbi.Table.TypeLazy ?? tbi.Table.Type;
if (tbiindex == 0)
blockExp.AddRange(new Expression[] {
Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(typei), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) })),
Expression.IfThen(
Expression.GreaterThan(readExpDataIndex, dataIndexExp),
Expression.Assign(dataIndexExp, readExpDataIndex)
),
Expression.IfThen(
Expression.NotEqual(readExpValue, Expression.Constant(null)),
Expression.Assign(retExp, Expression.Convert(readExpValue, typei))
)
});
else {
Expression curExpIfNotNull = Expression.IsTrue(Expression.Constant(true));
Expression curExp = retExp;
var curTb = tb;
var parentNameSplits = tbi.Alias.Split(new[] { "__" }, StringSplitOptions.None);
var iscontinue = false;
for (var k = 1; k < parentNameSplits.Length; k++) {
var curPropName = parentNameSplits[k];
if (curTb.Table.Properties.TryGetValue(parentNameSplits[k], out var tryprop) == false) {
k++;
curPropName = $"{curPropName}__{parentNameSplits[k]}";
if (curTb.Table.Properties.TryGetValue(parentNameSplits[k], out tryprop) == false) {
iscontinue = true;
break;
}
}
curExp = Expression.MakeMemberAccess(curExp, tryprop);
if (k + 1 < parentNameSplits.Length)
curExpIfNotNull = Expression.AndAlso(curExpIfNotNull, Expression.NotEqual(curExp, Expression.Default(tryprop.PropertyType)));
curTb = _tables.Where(a => a.Alias == $"{curTb.Alias}__{curPropName}" && a.Table.Type == tryprop.PropertyType).FirstOrDefault();
if (curTb == null) {
iscontinue = true;
break;
}
}
if (iscontinue) continue;
blockExp.Add(
Expression.IfThen(
curExpIfNotNull,
Expression.Block(new Expression[] {
Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(typei), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) })),
Expression.IfThen(
Expression.GreaterThan(readExpDataIndex, dataIndexExp),
Expression.Assign(dataIndexExp, readExpDataIndex)
),
Expression.IfThen(
Expression.NotEqual(readExpValue, Expression.Constant(null)),
Expression.Assign(curExp, Expression.Convert(readExpValue, typei))
)
})
)
);
}
if (tbi.Table.TypeLazy != null)
blockExp.Add(
Expression.IfThen(
Expression.NotEqual(readExpValue, Expression.Constant(null)),
Expression.Call(retExp, tbi.Table.TypeLazySetOrm, ormExp)
)
); //将 orm 传递给 lazy
var colidx = 0;
foreach (var col in tbi.Table.Columns.Values) {
if (index > 0) {
field.Append(", ");
if (tbiindex > 0 && colidx == 0) field.Append("\r\n");
}
var quoteName = _commonUtils.QuoteSqlName(col.Attribute.Name);
field.Append(_commonUtils.QuoteReadColumn(col.CsType, $"{tbi.Alias}.{quoteName}"));
++index;
if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index);
else dicfield.Add(quoteName, true);
++colidx;
}
tbiindex++;
}
blockExp.AddRange(new Expression[] {
Expression.Return(returnTarget, retExp),
Expression.Label(returnTarget, Expression.Default(type))
});
return new GetAllFieldExpressionTreeInfo {
Field = field.ToString(),
Read = Expression.Lambda<Func<IFreeSql, DbDataReader, T1>>(Expression.Block(new[] { retExp, dataIndexExp, readExp }, blockExp), new[] { ormExp, rowExp }).Compile()
};
});
}
protected GetAllFieldExpressionTreeInfo GetAllFieldExpressionTreeLevel2() {
return _dicGetAllFieldExpressionTree.GetOrAdd(string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Alias}-{a.Type}")), s => { return _dicGetAllFieldExpressionTree.GetOrAdd(string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Alias}-{a.Type}")), s => {
var tb1 = _tables.First().Table; var tb1 = _tables.First().Table;
var type = tb1.TypeLazy ?? tb1.Type; var type = tb1.TypeLazy ?? tb1.Type;
@ -390,7 +510,9 @@ namespace FreeSql.Internal.CommonProvider {
var tb2 = _tables.Where((a, b) => b > 0 && var tb2 = _tables.Where((a, b) => b > 0 &&
(a.Type == SelectTableInfoType.InnerJoin || a.Type == SelectTableInfoType.LeftJoin || a.Type == SelectTableInfoType.RightJoin) && (a.Type == SelectTableInfoType.InnerJoin || a.Type == SelectTableInfoType.LeftJoin || a.Type == SelectTableInfoType.RightJoin) &&
string.IsNullOrEmpty(a.On) == false && string.IsNullOrEmpty(a.On) == false &&
a.Alias.Contains(prop.Name)).FirstOrDefault(); //判断 b > 0 防止 parent 递归关系 a.Alias.StartsWith($"{tb.Alias}__") && //开头结尾完全匹配
a.Alias.EndsWith($"__{prop.Name}") //不清楚会不会有其他情况 求大佬优化
).FirstOrDefault(); //判断 b > 0 防止 parent 递归关系
if (tb2 == null && props.Where(pw => pw.Value.PropertyType == prop.PropertyType).Count() == 1) if (tb2 == null && props.Where(pw => pw.Value.PropertyType == prop.PropertyType).Count() == 1)
tb2 = _tables.Where((a, b) => b > 0 && tb2 = _tables.Where((a, b) => b > 0 &&
(a.Type == SelectTableInfoType.InnerJoin || a.Type == SelectTableInfoType.LeftJoin || a.Type == SelectTableInfoType.RightJoin) && (a.Type == SelectTableInfoType.InnerJoin || a.Type == SelectTableInfoType.LeftJoin || a.Type == SelectTableInfoType.RightJoin) &&
@ -501,7 +623,8 @@ namespace FreeSql.Internal.CommonProvider {
else dicfield.Add(quoteName, true); else dicfield.Add(quoteName, true);
child.Childs.Add(new ReadAnonymousTypeInfo { child.Childs.Add(new ReadAnonymousTypeInfo {
Property = tb2.Table.Type.GetProperty(col2.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance), Property = tb2.Table.Type.GetProperty(col2.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance),
CsName = col2.CsName }); CsName = col2.CsName
});
} }
} }
map.Childs.Add(child); map.Childs.Add(child);