Extend NPoco fluent querying

This commit is contained in:
Stephan
2018-09-25 10:55:06 +02:00
parent f4e87b9949
commit 5f805b0804
2 changed files with 125 additions and 0 deletions

View File

@@ -392,6 +392,23 @@ namespace Umbraco.Core.Persistence
#region Joins
/// <summary>
/// Appends a CROSS JOIN clause to the Sql statement.
/// </summary>
/// <typeparam name="TDto">The type of the Dto.</typeparam>
/// <param name="sql">The Sql statement.</param>
/// <param name="alias">An optional alias for the joined table.</param>
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> CrossJoin<TDto>(this Sql<ISqlContext> sql, string alias = null)
{
var type = typeof(TDto);
var tableName = type.GetTableName();
var join = sql.SqlContext.SqlSyntax.GetQuotedTableName(tableName);
if (alias != null) join += " " + sql.SqlContext.SqlSyntax.GetQuotedTableName(alias);
return sql.Append("CROSS JOIN " + join);
}
/// <summary>
/// Appends an INNER JOIN clause to the Sql statement.
/// </summary>
@@ -533,6 +550,25 @@ namespace Umbraco.Core.Persistence
return sqlJoin.On(onExpression, expresionist.GetSqlParameters());
}
/// <summary>
/// Appends an ON clause to a SqlJoin statement.
/// </summary>
/// <typeparam name="TDto1">The type of Dto 1.</typeparam>
/// <typeparam name="TDto2">The type of Dto 2.</typeparam>
/// <typeparam name="TDto3">The type of Dto 3.</typeparam>
/// <param name="sqlJoin">The SqlJoin statement.</param>
/// <param name="predicate">A predicate to transform and use as the ON clause body.</param>
/// <param name="aliasLeft">An optional alias for Dto 1 table.</param>
/// <param name="aliasRight">An optional alias for Dto 2 table.</param>
/// <param name="aliasOther">An optional alias for Dto 3 table.</param>
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> On<TDto1, TDto2, TDto3>(this Sql<ISqlContext>.SqlJoinClause<ISqlContext> sqlJoin, Expression<Func<TDto1, TDto2, TDto3, bool>> predicate, string aliasLeft = null, string aliasRight = null, string aliasOther = null)
{
var expresionist = new PocoToSqlExpressionVisitor<TDto1, TDto2, TDto3>(sqlJoin.SqlContext, aliasLeft, aliasRight, aliasOther);
var onExpression = expresionist.Visit(predicate);
return sqlJoin.On(onExpression, expresionist.GetSqlParameters());
}
#endregion
#region Select

View File

@@ -167,4 +167,93 @@ namespace Umbraco.Core.Persistence.Querying
}
}
/// <summary>
/// Represents an expression tree parser used to turn strongly typed expressions into SQL statements.
/// </summary>
/// <typeparam name="TDto1">The type of DTO 1.</typeparam>
/// <typeparam name="TDto2">The type of DTO 2.</typeparam>
/// <typeparam name="TDto3">The type of DTO 3.</typeparam>
/// <remarks>This visitor is stateful and cannot be reused.</remarks>
internal class PocoToSqlExpressionVisitor<TDto1, TDto2, TDto3> : ExpressionVisitorBase
{
private readonly PocoData _pocoData1, _pocoData2, _pocoData3;
private readonly string _alias1, _alias2, _alias3;
private string _parameterName1, _parameterName2, _parameterName3;
public PocoToSqlExpressionVisitor(ISqlContext sqlContext, string alias1, string alias2, string alias3)
: base(sqlContext.SqlSyntax)
{
_pocoData1 = sqlContext.PocoDataFactory.ForType(typeof(TDto1));
_pocoData2 = sqlContext.PocoDataFactory.ForType(typeof(TDto2));
_pocoData3 = sqlContext.PocoDataFactory.ForType(typeof(TDto3));
_alias1 = alias1;
_alias2 = alias2;
_alias3 = alias3;
}
protected override string VisitLambda(LambdaExpression lambda)
{
if (lambda.Parameters.Count == 3)
{
_parameterName1 = lambda.Parameters[0].Name;
_parameterName2 = lambda.Parameters[1].Name;
_parameterName3 = lambda.Parameters[2].Name;
}
else if (lambda.Parameters.Count == 2)
{
_parameterName1 = lambda.Parameters[0].Name;
_parameterName2 = lambda.Parameters[1].Name;
}
else
{
_parameterName1 = _parameterName2 = null;
}
return base.VisitLambda(lambda);
}
protected override string VisitMemberAccess(MemberExpression m)
{
if (m.Expression != null)
{
if (m.Expression.NodeType == ExpressionType.Parameter)
{
var pex = (ParameterExpression)m.Expression;
if (pex.Name == _parameterName1)
return Visited ? string.Empty : GetFieldName(_pocoData1, m.Member.Name, _alias1);
if (pex.Name == _parameterName2)
return Visited ? string.Empty : GetFieldName(_pocoData2, m.Member.Name, _alias2);
if (pex.Name == _parameterName3)
return Visited ? string.Empty : GetFieldName(_pocoData3, m.Member.Name, _alias3);
}
else if (m.Expression.NodeType == ExpressionType.Convert)
{
// here: which _pd should we use?!
throw new NotSupportedException();
//return Visited ? string.Empty : GetFieldName(_pd, m.Member.Name);
}
}
var member = Expression.Convert(m, typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
var o = getter();
SqlParameters.Add(o);
// execute if not already compiled
return Visited ? string.Empty : "@" + (SqlParameters.Count - 1);
}
protected virtual string GetFieldName(PocoData pocoData, string name, string alias)
{
var column = pocoData.Columns.FirstOrDefault(x => x.Value.MemberInfoData.Name == name);
var tableName = SqlSyntax.GetQuotedTableName(alias ?? pocoData.TableInfo.TableName);
var columnName = SqlSyntax.GetQuotedColumnName(column.Value.ColumnName);
return tableName + "." + columnName;
}
}
}