CQRS_Simple/CQRS_Simple.Infrastructure/Dapper/DynamicQuery.cs

201 lines
7.9 KiB
C#
Raw Permalink Normal View History

2022-04-07 14:00:58 +08:00
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace CQRS_Simple.Infrastructure.Dapper
{
/// <summary>
/// Dynamic query class.
/// </summary>
public sealed class DynamicQuery
{
/// <summary>
/// Gets the insert query.
/// </summary>
/// <param name="tableName">Name of the table.</param>
/// <param name="item">The item.</param>
/// <returns>
/// The Sql query based on the item properties.
/// </returns>
public static string GetInsertQuery(string tableName, object item)
{
PropertyInfo[] props = item.GetType().GetProperties(
BindingFlags.Public |
BindingFlags.Instance);
var columns = props.Where(x => x.Name != "Id").Select(p => p.Name).ToArray();
return string.Format("INSERT INTO {0} ({1}) OUTPUT Inserted.Id VALUES (@{2})",
tableName,
string.Join(",", columns),
string.Join(",@", columns));
}
/// <summary>
/// Gets the update query.
/// </summary>
/// <param name="tableName">Name of the table.</param>
/// <param name="item">The item.</param>
/// <returns>
/// The Sql query based on the item properties.
/// </returns>
public static string GetUpdateQuery(string tableName, dynamic item)
{
PropertyInfo[] props = item.GetType().GetProperties(BindingFlags.Public |
BindingFlags.Instance);
string[] columns = props.Where(x => x.Name != "Id").Select(p => p.Name).ToArray();
var parameters = columns.Select(name => name + "=@" + name).ToList();
return string.Format("UPDATE {0} SET {1} WHERE Id=@Id", tableName, string.Join(",", parameters));
}
/// <summary>
/// Gets the dynamic query.
/// </summary>
/// <param name="tableName">Name of the table.</param>
/// <param name="expression">The expression.</param>
/// <returns>A result object with the generated sql and dynamic params.</returns>
public static QueryResult GetDynamicQuery<T>(string tableName, Expression<Func<T, bool>> expression)
{
var queryProperties = new List<QueryParameter>();
var body = (BinaryExpression)expression.Body;
IDictionary<string, Object> expando = new ExpandoObject();
var builder = new StringBuilder();
// walk the tree and build up a list of query parameter objects
// from the left and right branches of the expression tree
WalkTree(body, ExpressionType.Default, ref queryProperties);
// convert the query parms into a SQL string and dynamic property object
builder.Append("SELECT * FROM ");
builder.Append(tableName);
builder.Append(" WHERE ");
for (int i = 0; i < queryProperties.Count(); i++)
{
QueryParameter item = queryProperties[i];
if (!string.IsNullOrEmpty(item.LinkingOperator) && i > 0)
{
builder.Append(string.Format("{0} {1} {2} @{1} ", item.LinkingOperator, item.PropertyName,
item.QueryOperator));
}
else
{
builder.Append(string.Format("{0} {1} @{0} ", item.PropertyName, item.QueryOperator));
}
expando[item.PropertyName] = item.PropertyValue;
}
return new QueryResult(builder.ToString().TrimEnd(), expando);
}
/// <summary>
/// Walks the tree.
/// </summary>
/// <param name="body">The body.</param>
/// <param name="linkingType">Type of the linking.</param>
/// <param name="queryProperties">The query properties.</param>
private static void WalkTree(BinaryExpression body, ExpressionType linkingType,
ref List<QueryParameter> queryProperties)
{
if (body.NodeType != ExpressionType.AndAlso && body.NodeType != ExpressionType.OrElse)
{
string propertyName = GetPropertyName(body);
dynamic propertyValue = body.Right;
string opr = GetOperator(body.NodeType);
string link = GetOperator(linkingType);
queryProperties.Add(new QueryParameter(link, propertyName, propertyValue.Value, opr));
}
else
{
WalkTree((BinaryExpression)body.Left, body.NodeType, ref queryProperties);
WalkTree((BinaryExpression)body.Right, body.NodeType, ref queryProperties);
}
}
/// <summary>
/// Gets the name of the property.
/// </summary>
/// <param name="body">The body.</param>
/// <returns>The property name for the property expression.</returns>
private static string GetPropertyName(BinaryExpression body)
{
string propertyName = body.Left.ToString().Split(new char[] { '.' })[1];
if (body.Left.NodeType == ExpressionType.Convert)
{
// hack to remove the trailing ) when convering.
propertyName = propertyName.Replace(")", string.Empty);
}
return propertyName;
}
/// <summary>
/// Gets the operator.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>
/// The expression types SQL server equivalent operator.
/// </returns>
/// <exception cref="System.NotImplementedException"></exception>
private static string GetOperator(ExpressionType type)
{
switch (type)
{
case ExpressionType.Equal:
return "=";
case ExpressionType.NotEqual:
return "!=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.AndAlso:
case ExpressionType.And:
return "AND";
case ExpressionType.Or:
case ExpressionType.OrElse:
return "OR";
case ExpressionType.Default:
return string.Empty;
default:
throw new NotImplementedException();
}
}
}
/// <summary>
/// Class that models the data structure in coverting the expression tree into SQL and Params.
/// </summary>
internal class QueryParameter
{
public string LinkingOperator { get; set; }
public string PropertyName { get; set; }
public object PropertyValue { get; set; }
public string QueryOperator { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="QueryParameter" /> class.
/// </summary>
/// <param name="linkingOperator">The linking operator.</param>
/// <param name="propertyName">Name of the property.</param>
/// <param name="propertyValue">The property value.</param>
/// <param name="queryOperator">The query operator.</param>
internal QueryParameter(string linkingOperator, string propertyName, object propertyValue, string queryOperator)
{
this.LinkingOperator = linkingOperator;
this.PropertyName = propertyName;
this.PropertyValue = propertyValue;
this.QueryOperator = queryOperator;
}
}
}