Radarr/src/Marr.Data/QGen/SortBuilder.cs

263 lines
8.0 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Linq.Expressions;
using Marr.Data.QGen.Dialects;
namespace Marr.Data.QGen
{
/// <summary>
/// This class is responsible for creating an "ORDER BY" clause.
/// It uses chaining methods to provide a fluent interface.
/// It also has some methods that coincide with Linq methods, to provide Linq compatibility.
/// </summary>
/// <typeparam name="T"></typeparam>
public class SortBuilder<T> : IEnumerable<T>, ISortQueryBuilder
{
private string _constantOrderByClause;
private QueryBuilder<T> _baseBuilder;
private Dialect _dialect;
private List<SortColumn<T>> _sortExpressions;
private bool _useAltName;
private TableCollection _tables;
private IDataMapper _db;
private WhereBuilder<T> _whereBuilder;
public SortBuilder()
{
// Used only for unit testing with mock frameworks
}
public SortBuilder(QueryBuilder<T> baseBuilder, IDataMapper db, WhereBuilder<T> whereBuilder, Dialect dialect, TableCollection tables, bool useAltName)
{
_baseBuilder = baseBuilder;
_db = db;
_whereBuilder = whereBuilder;
_dialect = dialect;
_sortExpressions = new List<SortColumn<T>>();
_useAltName = useAltName;
_tables = tables;
}
#region - AndWhere / OrWhere -
public virtual SortBuilder<T> OrWhere(Expression<Func<T, bool>> filterExpression)
{
var orWhere = new WhereBuilder<T>(_db.Command, _dialect, filterExpression, _tables, false, true);
_whereBuilder.Append(orWhere, WhereAppendType.OR);
return this;
}
public virtual SortBuilder<T> OrWhere(string whereClause)
{
var orWhere = new WhereBuilder<T>(whereClause, false);
_whereBuilder.Append(orWhere, WhereAppendType.OR);
return this;
}
public virtual SortBuilder<T> AndWhere(Expression<Func<T, bool>> filterExpression)
{
var andWhere = new WhereBuilder<T>(_db.Command, _dialect, filterExpression, _tables, false, true);
_whereBuilder.Append(andWhere, WhereAppendType.AND);
return this;
}
public virtual SortBuilder<T> AndWhere(string whereClause)
{
var andWhere = new WhereBuilder<T>(whereClause, false);
_whereBuilder.Append(andWhere, WhereAppendType.AND);
return this;
}
#endregion
#region - Order -
internal SortBuilder<T> Order(Type declaringType, string propertyName)
{
_sortExpressions.Add(new SortColumn<T>(declaringType, propertyName, SortDirection.Asc));
return this;
}
internal SortBuilder<T> OrderByDescending(Type declaringType, string propertyName)
{
_sortExpressions.Add(new SortColumn<T>(declaringType, propertyName, SortDirection.Desc));
return this;
}
public virtual SortBuilder<T> OrderBy(string orderByClause)
{
if (string.IsNullOrEmpty(orderByClause))
throw new ArgumentNullException("orderByClause");
if (!orderByClause.ToUpper().Contains("ORDER BY "))
{
orderByClause = orderByClause.Insert(0, " ORDER BY ");
}
_constantOrderByClause = orderByClause;
return this;
}
public virtual SortBuilder<T> OrderBy(Expression<Func<T, object>> sortExpression)
{
_sortExpressions.Add(new SortColumn<T>(sortExpression, SortDirection.Asc));
return this;
}
public virtual SortBuilder<T> OrderBy(Expression<Func<T, object>> sortExpression, SortDirection sortDirection)
{
_sortExpressions.Add(new SortColumn<T>(sortExpression, sortDirection));
return this;
}
public virtual SortBuilder<T> OrderByDescending(Expression<Func<T, object>> sortExpression)
{
_sortExpressions.Add(new SortColumn<T>(sortExpression, SortDirection.Desc));
return this;
}
public virtual SortBuilder<T> ThenBy(Expression<Func<T, object>> sortExpression)
{
_sortExpressions.Add(new SortColumn<T>(sortExpression, SortDirection.Asc));
return this;
}
public virtual SortBuilder<T> ThenBy(Expression<Func<T, object>> sortExpression, SortDirection sortDirection)
{
_sortExpressions.Add(new SortColumn<T>(sortExpression, sortDirection));
return this;
}
public virtual SortBuilder<T> ThenByDescending(Expression<Func<T, object>> sortExpression)
{
_sortExpressions.Add(new SortColumn<T>(sortExpression, SortDirection.Desc));
return this;
}
#endregion
#region - Paging -
public virtual SortBuilder<T> Take(int count)
{
_baseBuilder.Take(count);
return this;
}
public virtual SortBuilder<T> Skip(int count)
{
_baseBuilder.Skip(count);
return this;
}
public virtual SortBuilder<T> Page(int pageNumber, int pageSize)
{
_baseBuilder.Page(pageNumber, pageSize);
return this;
}
#endregion
#region - GetRowCount -
public virtual int GetRowCount()
{
return _baseBuilder.GetRowCount();
}
#endregion
#region - ToList / ToString / BuildQuery -
public virtual List<T> ToList()
{
return _baseBuilder.ToList();
}
public virtual string BuildQuery()
{
return _baseBuilder.BuildQuery();
}
public virtual string BuildQuery(bool useAltName)
{
if (!string.IsNullOrEmpty(_constantOrderByClause))
{
return _constantOrderByClause;
}
StringBuilder sb = new StringBuilder();
foreach (var sort in _sortExpressions)
{
if (sb.Length > 0)
sb.Append(",");
Table table = _tables.FindTable(sort.DeclaringType);
if (table == null)
{
string msg = string.Format("The property '{0} -> {1}' you are trying to reference in the 'ORDER BY' statement belongs to an entity that has not been joined in your query. To reference this property, you must join the '{0}' entity using the Join method.",
sort.DeclaringType.Name,
sort.PropertyName);
throw new DataMappingException(msg);
}
string columnName = DataHelper.GetColumnName(sort.DeclaringType, sort.PropertyName, useAltName);
if (!useAltName)
sb.Append(_dialect.CreateToken(string.Format("{0}.{1}", table.Alias, columnName)));
else
sb.Append(_dialect.CreateToken(string.Format("{0}", columnName)));
if (sort.Direction == SortDirection.Desc)
sb.Append(" DESC");
}
if (sb.Length > 0)
sb.Insert(0, " ORDER BY ");
return sb.ToString();
}
public override string ToString()
{
return BuildQuery(_useAltName);
}
#endregion
#region - Implicit List<T> Operator -
public static implicit operator List<T>(SortBuilder<T> builder)
{
return builder.ToList();
}
#endregion
#region IEnumerable<T> Members
public virtual IEnumerator<T> GetEnumerator()
{
var list = ToList();
return list.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
}