mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-17 21:26:22 -04:00
added marr.datamapper source code for easy debugging.
This commit is contained in:
33
Marr.Data/QGen/DeleteQuery.cs
Normal file
33
Marr.Data/QGen/DeleteQuery.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.Mapping;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class creates a SQL delete query.
|
||||
/// </summary>
|
||||
public class DeleteQuery : IQuery
|
||||
{
|
||||
protected Table TargetTable { get; set; }
|
||||
protected string WhereClause { get; set; }
|
||||
protected Dialects.Dialect Dialect { get; set; }
|
||||
|
||||
public DeleteQuery(Dialects.Dialect dialect, Table targetTable, string whereClause)
|
||||
{
|
||||
Dialect = dialect;
|
||||
TargetTable = targetTable;
|
||||
WhereClause = whereClause;
|
||||
}
|
||||
|
||||
public string Generate()
|
||||
{
|
||||
return string.Format("DELETE FROM {0} {1} ",
|
||||
Dialect.CreateToken(TargetTable.Name),
|
||||
WhereClause);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Marr.Data/QGen/Dialects/Dialect.cs
Normal file
59
Marr.Data/QGen/Dialects/Dialect.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Marr.Data.QGen.Dialects
|
||||
{
|
||||
public class Dialect
|
||||
{
|
||||
/// <summary>
|
||||
/// The default token is surrounded by brackets.
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
public virtual string CreateToken(string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string[] parts = token.Replace('[', new Char()).Replace(']', new Char()).Split('.');
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (string part in parts)
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
sb.Append(".");
|
||||
|
||||
sb.Append("[").Append(part).Append("]");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public virtual string IdentityQuery
|
||||
{
|
||||
get
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasIdentityQuery
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrEmpty(IdentityQuery);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool SupportsBatchQueries
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Marr.Data/QGen/Dialects/FirebirdDialect.cs
Normal file
20
Marr.Data/QGen/Dialects/FirebirdDialect.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Marr.Data.QGen.Dialects
|
||||
{
|
||||
public class FirebirdDialect : Dialect
|
||||
{
|
||||
public override string CreateToken(string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return token.Replace('[', new Char()).Replace(']', new Char());
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Marr.Data/QGen/Dialects/OracleDialect.cs
Normal file
36
Marr.Data/QGen/Dialects/OracleDialect.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Marr.Data.QGen.Dialects
|
||||
{
|
||||
public class OracleDialect : Dialect
|
||||
{
|
||||
public override string CreateToken(string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string[] parts = token.Replace('[', new Char()).Replace(']', new Char()).Split('.');
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (string part in parts)
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
sb.Append(".");
|
||||
|
||||
bool hasSpaces = part.Contains(' ');
|
||||
|
||||
if (hasSpaces)
|
||||
sb.Append("[").Append(part).Append("]");
|
||||
else
|
||||
sb.Append(part);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Marr.Data/QGen/Dialects/SqlServerCeDialect.cs
Normal file
26
Marr.Data/QGen/Dialects/SqlServerCeDialect.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Marr.Data.QGen.Dialects
|
||||
{
|
||||
public class SqlServerCeDialect : Dialect
|
||||
{
|
||||
public override string IdentityQuery
|
||||
{
|
||||
get
|
||||
{
|
||||
return "SELECT @@IDENTITY;";
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsBatchQueries
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Marr.Data/QGen/Dialects/SqlServerDialect.cs
Normal file
18
Marr.Data/QGen/Dialects/SqlServerDialect.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Marr.Data.QGen.Dialects
|
||||
{
|
||||
public class SqlServerDialect : Dialect
|
||||
{
|
||||
public override string IdentityQuery
|
||||
{
|
||||
get
|
||||
{
|
||||
return "SELECT SCOPE_IDENTITY();";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Marr.Data/QGen/Dialects/SqliteDialect.cs
Normal file
18
Marr.Data/QGen/Dialects/SqliteDialect.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Marr.Data.QGen.Dialects
|
||||
{
|
||||
public class SqliteDialect : Dialect
|
||||
{
|
||||
public override string IdentityQuery
|
||||
{
|
||||
get
|
||||
{
|
||||
return "SELECT last_insert_rowid();";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
Marr.Data/QGen/ExpressionVisitor.cs
Normal file
149
Marr.Data/QGen/ExpressionVisitor.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
/* This class was copied from Mehfuz's LinqExtender project, which is available from github.
|
||||
* http://mehfuzh.github.com/LinqExtender/
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
///<summary>
|
||||
/// Expression visitor
|
||||
///</summary>
|
||||
public class ExpressionVisitor
|
||||
{
|
||||
/// <summary>
|
||||
/// Visits expression and delegates call to different to branch.
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Expression Visit(Expression expression)
|
||||
{
|
||||
if (expression == null)
|
||||
return null;
|
||||
|
||||
switch (expression.NodeType)
|
||||
{
|
||||
case ExpressionType.Lambda:
|
||||
return VisitLamda((LambdaExpression)expression);
|
||||
case ExpressionType.ArrayLength:
|
||||
case ExpressionType.Convert:
|
||||
case ExpressionType.ConvertChecked:
|
||||
case ExpressionType.Negate:
|
||||
case ExpressionType.UnaryPlus:
|
||||
case ExpressionType.NegateChecked:
|
||||
case ExpressionType.Not:
|
||||
case ExpressionType.Quote:
|
||||
case ExpressionType.TypeAs:
|
||||
return this.VisitUnary((UnaryExpression)expression);
|
||||
case ExpressionType.Add:
|
||||
case ExpressionType.AddChecked:
|
||||
case ExpressionType.And:
|
||||
case ExpressionType.AndAlso:
|
||||
case ExpressionType.ArrayIndex:
|
||||
case ExpressionType.Coalesce:
|
||||
case ExpressionType.Divide:
|
||||
case ExpressionType.Equal:
|
||||
case ExpressionType.ExclusiveOr:
|
||||
case ExpressionType.GreaterThan:
|
||||
case ExpressionType.GreaterThanOrEqual:
|
||||
case ExpressionType.LeftShift:
|
||||
case ExpressionType.LessThan:
|
||||
case ExpressionType.LessThanOrEqual:
|
||||
case ExpressionType.Modulo:
|
||||
case ExpressionType.Multiply:
|
||||
case ExpressionType.MultiplyChecked:
|
||||
case ExpressionType.NotEqual:
|
||||
case ExpressionType.Or:
|
||||
case ExpressionType.OrElse:
|
||||
case ExpressionType.Power:
|
||||
case ExpressionType.RightShift:
|
||||
case ExpressionType.Subtract:
|
||||
case ExpressionType.SubtractChecked:
|
||||
return this.VisitBinary((BinaryExpression)expression);
|
||||
case ExpressionType.Call:
|
||||
return this.VisitMethodCall((MethodCallExpression)expression);
|
||||
case ExpressionType.Constant:
|
||||
return this.VisitConstant((ConstantExpression)expression);
|
||||
case ExpressionType.MemberAccess:
|
||||
return this.VisitMemberAccess((MemberExpression)expression);
|
||||
case ExpressionType.Parameter:
|
||||
return this.VisitParameter((ParameterExpression)expression);
|
||||
|
||||
}
|
||||
throw new ArgumentOutOfRangeException("expression", expression.NodeType.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visits the constance expression. To be implemented by user.
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Expression VisitConstant(ConstantExpression expression)
|
||||
{
|
||||
return expression;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visits the memeber access expression. To be implemented by user.
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Expression VisitMemberAccess(MemberExpression expression)
|
||||
{
|
||||
return expression;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visits the method call expression. To be implemented by user.
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Expression VisitMethodCall(MethodCallExpression expression)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visits the binary expression.
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Expression VisitBinary(BinaryExpression expression)
|
||||
{
|
||||
this.Visit(expression.Left);
|
||||
this.Visit(expression.Right);
|
||||
return expression;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visits the unary expression.
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Expression VisitUnary(UnaryExpression expression)
|
||||
{
|
||||
this.Visit(expression.Operand);
|
||||
return expression;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visits the lamda expression.
|
||||
/// </summary>
|
||||
/// <param name="lambdaExpression"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Expression VisitLamda(LambdaExpression lambdaExpression)
|
||||
{
|
||||
this.Visit(lambdaExpression.Body);
|
||||
return lambdaExpression;
|
||||
}
|
||||
|
||||
private Expression VisitParameter(ParameterExpression expression)
|
||||
{
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Marr.Data/QGen/IQuery.cs
Normal file
16
Marr.Data/QGen/IQuery.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
internal interface IQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a SQL query for a given entity.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
string Generate();
|
||||
}
|
||||
}
|
||||
17
Marr.Data/QGen/IQueryBuilder.cs
Normal file
17
Marr.Data/QGen/IQueryBuilder.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
public interface IQueryBuilder
|
||||
{
|
||||
string BuildQuery();
|
||||
}
|
||||
|
||||
public interface ISortQueryBuilder : IQueryBuilder
|
||||
{
|
||||
string BuildQuery(bool useAltNames);
|
||||
}
|
||||
}
|
||||
70
Marr.Data/QGen/InsertQuery.cs
Normal file
70
Marr.Data/QGen/InsertQuery.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.Mapping;
|
||||
using System.Data.Common;
|
||||
using Marr.Data.QGen.Dialects;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class creates an insert query.
|
||||
/// </summary>
|
||||
public class InsertQuery : IQuery
|
||||
{
|
||||
protected Dialect Dialect { get; set; }
|
||||
protected string Target { get; set; }
|
||||
protected ColumnMapCollection Columns { get; set; }
|
||||
protected DbCommand Command { get; set; }
|
||||
|
||||
public InsertQuery(Dialect dialect, ColumnMapCollection columns, DbCommand command, string target)
|
||||
{
|
||||
if (string.IsNullOrEmpty(target))
|
||||
{
|
||||
throw new DataMappingException("A target table must be passed in or set in a TableAttribute.");
|
||||
}
|
||||
Dialect = dialect;
|
||||
Target = target;
|
||||
Columns = columns;
|
||||
Command = command;
|
||||
}
|
||||
|
||||
public virtual string Generate()
|
||||
{
|
||||
StringBuilder sql = new StringBuilder();
|
||||
StringBuilder values = new StringBuilder(") VALUES (");
|
||||
|
||||
sql.AppendFormat("INSERT INTO {0} (", Dialect.CreateToken(Target));
|
||||
|
||||
int sqlStartIndex = sql.Length;
|
||||
int valuesStartIndex = values.Length;
|
||||
|
||||
foreach (DbParameter p in Command.Parameters)
|
||||
{
|
||||
var c = Columns.GetByColumnName(p.ParameterName);
|
||||
|
||||
if (c == null)
|
||||
break; // All insert columns have been added
|
||||
|
||||
if (sql.Length > sqlStartIndex)
|
||||
sql.Append(",");
|
||||
|
||||
if (values.Length > valuesStartIndex)
|
||||
values.Append(",");
|
||||
|
||||
if (!c.ColumnInfo.IsAutoIncrement)
|
||||
{
|
||||
sql.AppendFormat(Dialect.CreateToken(c.ColumnInfo.Name));
|
||||
values.AppendFormat("{0}{1}", Command.ParameterPrefix(), p.ParameterName);
|
||||
}
|
||||
}
|
||||
|
||||
values.Append(")");
|
||||
|
||||
sql.Append(values);
|
||||
|
||||
return sql.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
204
Marr.Data/QGen/InsertQueryBuilder.cs
Normal file
204
Marr.Data/QGen/InsertQueryBuilder.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.Mapping;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
public class InsertQueryBuilder<T> : IQueryBuilder
|
||||
{
|
||||
private DataMapper _db;
|
||||
private string _tableName;
|
||||
private T _entity;
|
||||
private MappingHelper _mappingHelper;
|
||||
private ColumnMapCollection _mappings;
|
||||
private SqlModes _previousSqlMode;
|
||||
private bool _generateQuery = true;
|
||||
private bool _getIdentityValue;
|
||||
private Dialects.Dialect _dialect;
|
||||
private ColumnMapCollection _columnsToInsert;
|
||||
|
||||
public InsertQueryBuilder()
|
||||
{
|
||||
// Used only for unit testing with mock frameworks
|
||||
}
|
||||
|
||||
public InsertQueryBuilder(DataMapper db)
|
||||
{
|
||||
_db = db;
|
||||
_tableName = MapRepository.Instance.GetTableName(typeof(T));
|
||||
_previousSqlMode = _db.SqlMode;
|
||||
_mappingHelper = new MappingHelper(_db);
|
||||
_mappings = MapRepository.Instance.GetColumns(typeof(T));
|
||||
_dialect = QueryFactory.CreateDialect(_db);
|
||||
}
|
||||
|
||||
public virtual InsertQueryBuilder<T> TableName(string tableName)
|
||||
{
|
||||
_tableName = tableName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual InsertQueryBuilder<T> QueryText(string queryText)
|
||||
{
|
||||
_generateQuery = false;
|
||||
_db.Command.CommandText = queryText;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual InsertQueryBuilder<T> Entity(T entity)
|
||||
{
|
||||
_entity = entity;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs an identity query to get the value of an autoincrement field.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual InsertQueryBuilder<T> GetIdentity()
|
||||
{
|
||||
if (!_dialect.HasIdentityQuery)
|
||||
{
|
||||
string err = string.Format("The current dialect '{0}' does not have an identity query implemented.", _dialect.ToString());
|
||||
throw new DataMappingException(err);
|
||||
}
|
||||
|
||||
_getIdentityValue = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual InsertQueryBuilder<T> ColumnsIncluding(params Expression<Func<T, object>>[] properties)
|
||||
{
|
||||
List<string> columnList = new List<string>();
|
||||
|
||||
foreach (var column in properties)
|
||||
{
|
||||
columnList.Add(column.GetMemberName());
|
||||
}
|
||||
|
||||
return ColumnsIncluding(columnList.ToArray());
|
||||
}
|
||||
|
||||
public virtual InsertQueryBuilder<T> ColumnsIncluding(params string[] properties)
|
||||
{
|
||||
_columnsToInsert = new ColumnMapCollection();
|
||||
|
||||
foreach (string propertyName in properties)
|
||||
{
|
||||
_columnsToInsert.Add(_mappings.GetByFieldName(propertyName));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual InsertQueryBuilder<T> ColumnsExcluding(params Expression<Func<T, object>>[] properties)
|
||||
{
|
||||
List<string> columnList = new List<string>();
|
||||
|
||||
foreach (var column in properties)
|
||||
{
|
||||
columnList.Add(column.GetMemberName());
|
||||
}
|
||||
|
||||
return ColumnsExcluding(columnList.ToArray());
|
||||
}
|
||||
|
||||
public virtual InsertQueryBuilder<T> ColumnsExcluding(params string[] properties)
|
||||
{
|
||||
_columnsToInsert = new ColumnMapCollection();
|
||||
|
||||
_columnsToInsert.AddRange(_mappings);
|
||||
|
||||
foreach (string propertyName in properties)
|
||||
{
|
||||
_columnsToInsert.RemoveAll(c => c.FieldName == propertyName);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual object Execute()
|
||||
{
|
||||
if (_generateQuery)
|
||||
{
|
||||
BuildQuery();
|
||||
}
|
||||
else
|
||||
{
|
||||
TryAppendIdentityQuery();
|
||||
_mappingHelper.CreateParameters<T>(_entity, _mappings.NonReturnValues, _generateQuery);
|
||||
}
|
||||
|
||||
object scalar = null;
|
||||
|
||||
try
|
||||
{
|
||||
_db.OpenConnection();
|
||||
|
||||
scalar = _db.Command.ExecuteScalar();
|
||||
|
||||
if (_getIdentityValue && !_dialect.SupportsBatchQueries)
|
||||
{
|
||||
// Run identity query as a separate query
|
||||
_db.Command.CommandText = _dialect.IdentityQuery;
|
||||
scalar = _db.Command.ExecuteScalar();
|
||||
}
|
||||
|
||||
_mappingHelper.SetOutputValues<T>(_entity, _mappings.OutputFields);
|
||||
if (scalar != null)
|
||||
{
|
||||
_mappingHelper.SetOutputValues<T>(_entity, _mappings.ReturnValues, scalar);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_db.CloseConnection();
|
||||
}
|
||||
|
||||
|
||||
if (_generateQuery)
|
||||
{
|
||||
// Return to previous sql mode
|
||||
_db.SqlMode = _previousSqlMode;
|
||||
}
|
||||
|
||||
return scalar;
|
||||
}
|
||||
|
||||
public virtual string BuildQuery()
|
||||
{
|
||||
if (_entity == null)
|
||||
throw new ArgumentNullException("You must specify an entity to insert.");
|
||||
|
||||
// Override SqlMode since we know this will be a text query
|
||||
_db.SqlMode = SqlModes.Text;
|
||||
|
||||
var columns = _columnsToInsert ?? _mappings;
|
||||
|
||||
_mappingHelper.CreateParameters<T>(_entity, columns, _generateQuery);
|
||||
IQuery query = QueryFactory.CreateInsertQuery(columns, _db, _tableName);
|
||||
|
||||
_db.Command.CommandText = query.Generate();
|
||||
|
||||
TryAppendIdentityQuery();
|
||||
|
||||
return _db.Command.CommandText;
|
||||
}
|
||||
|
||||
private void TryAppendIdentityQuery()
|
||||
{
|
||||
if (_getIdentityValue && _dialect.SupportsBatchQueries)
|
||||
{
|
||||
// Append a batched identity query
|
||||
if (!_db.Command.CommandText.EndsWith(";"))
|
||||
{
|
||||
_db.Command.CommandText += ";";
|
||||
}
|
||||
_db.Command.CommandText += _dialect.IdentityQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Marr.Data/QGen/JoinBuilder.cs
Normal file
39
Marr.Data/QGen/JoinBuilder.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Data.Common;
|
||||
using Marr.Data.QGen.Dialects;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class overrides the WhereBuilder which utilizes the ExpressionVisitor base class,
|
||||
/// and it is responsible for translating the lambda expression into a "JOIN ON" clause.
|
||||
/// It populates the protected string builder, which outputs the "JOIN ON" clause when the ToString method is called.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The entity that is on the left side of the join.</typeparam>
|
||||
/// <typeparam name="T2">The entity that is on the right side of the join.</typeparam>
|
||||
public class JoinBuilder<T, T2> : WhereBuilder<T>
|
||||
{
|
||||
public JoinBuilder(DbCommand command, Dialect dialect, Expression<Func<T, T2, bool>> filter, TableCollection tables)
|
||||
: base(command, dialect, filter.Body, tables, false, true)
|
||||
{ }
|
||||
|
||||
protected override string PrefixText
|
||||
{
|
||||
get
|
||||
{
|
||||
return "ON";
|
||||
}
|
||||
}
|
||||
|
||||
protected override Expression VisitMemberAccess(MemberExpression expression)
|
||||
{
|
||||
string fqColumn = GetFullyQualifiedColumnName(expression.Member, expression.Expression.Type);
|
||||
_sb.Append(fqColumn);
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
239
Marr.Data/QGen/PagingQueryDecorator.cs
Normal file
239
Marr.Data/QGen/PagingQueryDecorator.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.Mapping;
|
||||
using Marr.Data.QGen.Dialects;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// Decorates the SelectQuery by wrapping it in a paging query.
|
||||
/// </summary>
|
||||
public class PagingQueryDecorator : IQuery
|
||||
{
|
||||
private SelectQuery _innerQuery;
|
||||
private int _firstRow;
|
||||
private int _lastRow;
|
||||
|
||||
public PagingQueryDecorator(SelectQuery innerQuery, int skip, int take)
|
||||
{
|
||||
if (string.IsNullOrEmpty(innerQuery.OrderBy.ToString()))
|
||||
{
|
||||
throw new DataMappingException("A paged query must specify an order by clause.");
|
||||
}
|
||||
|
||||
_innerQuery = innerQuery;
|
||||
_firstRow = skip + 1;
|
||||
_lastRow = skip + take;
|
||||
}
|
||||
|
||||
public string Generate()
|
||||
{
|
||||
// Decide which type of paging query to create
|
||||
|
||||
if (_innerQuery.IsView || _innerQuery.IsJoin)
|
||||
{
|
||||
return ComplexPaging();
|
||||
}
|
||||
else
|
||||
{
|
||||
return SimplePaging();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a query that pages a simple inner query.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string SimplePaging()
|
||||
{
|
||||
// Create paged query
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
sql.AppendLine("WITH RowNumCTE AS");
|
||||
sql.AppendLine("(");
|
||||
_innerQuery.BuildSelectClause(sql);
|
||||
BuildRowNumberColumn(sql);
|
||||
_innerQuery.BuildFromClause(sql);
|
||||
_innerQuery.BuildJoinClauses(sql);
|
||||
_innerQuery.BuildWhereClause(sql);
|
||||
sql.AppendLine(")");
|
||||
BuildSimpleOuterSelect(sql);
|
||||
|
||||
return sql.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a query that pages a view or joined inner query.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string ComplexPaging()
|
||||
{
|
||||
// Create paged query
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
sql.AppendLine("WITH GroupCTE AS (");
|
||||
BuildSelectClause(sql);
|
||||
BuildGroupColumn(sql);
|
||||
_innerQuery.BuildFromClause(sql);
|
||||
_innerQuery.BuildJoinClauses(sql);
|
||||
_innerQuery.BuildWhereClause(sql);
|
||||
sql.AppendLine("),");
|
||||
sql.AppendLine("RowNumCTE AS (");
|
||||
sql.AppendLine("SELECT *");
|
||||
BuildRowNumberColumn(sql);
|
||||
sql.AppendLine("FROM GroupCTE");
|
||||
sql.AppendLine("WHERE GroupRow = 1");
|
||||
sql.AppendLine(")");
|
||||
_innerQuery.BuildSelectClause(sql);
|
||||
_innerQuery.BuildFromClause(sql);
|
||||
_innerQuery.BuildJoinClauses(sql);
|
||||
BuildJoinBackToCTE(sql);
|
||||
sql.AppendFormat("WHERE RowNumber BETWEEN {0} AND {1}", _firstRow, _lastRow);
|
||||
|
||||
return sql.ToString();
|
||||
}
|
||||
|
||||
private void BuildJoinBackToCTE(StringBuilder sql)
|
||||
{
|
||||
Table baseTable = GetBaseTable();
|
||||
sql.AppendLine("INNER JOIN RowNumCTE cte");
|
||||
int pksAdded = 0;
|
||||
foreach (var pk in baseTable.Columns.PrimaryKeys)
|
||||
{
|
||||
if (pksAdded > 0)
|
||||
sql.Append(" AND ");
|
||||
|
||||
string cteQueryPkName = _innerQuery.NameOrAltName(pk.ColumnInfo);
|
||||
string outerQueryPkName = _innerQuery.IsJoin ? pk.ColumnInfo.Name : _innerQuery.NameOrAltName(pk.ColumnInfo);
|
||||
sql.AppendFormat("ON cte.{0} = {1} ", cteQueryPkName, _innerQuery.Dialect.CreateToken(string.Concat("t0", ".", outerQueryPkName)));
|
||||
pksAdded++;
|
||||
}
|
||||
sql.AppendLine();
|
||||
}
|
||||
|
||||
private void BuildSimpleOuterSelect(StringBuilder sql)
|
||||
{
|
||||
sql.Append("SELECT ");
|
||||
int startIndex = sql.Length;
|
||||
|
||||
// COLUMNS
|
||||
foreach (Table join in _innerQuery.Tables)
|
||||
{
|
||||
for (int i = 0; i < join.Columns.Count; i++)
|
||||
{
|
||||
var c = join.Columns[i];
|
||||
|
||||
if (sql.Length > startIndex)
|
||||
sql.Append(",");
|
||||
|
||||
string token = _innerQuery.NameOrAltName(c.ColumnInfo);
|
||||
sql.Append(_innerQuery.Dialect.CreateToken(token));
|
||||
}
|
||||
}
|
||||
|
||||
sql.AppendLine("FROM RowNumCTE");
|
||||
sql.AppendFormat("WHERE RowNumber BETWEEN {0} AND {1}", _firstRow, _lastRow).AppendLine();
|
||||
sql.AppendLine("ORDER BY RowNumber ASC;");
|
||||
}
|
||||
|
||||
private void BuildGroupColumn(StringBuilder sql)
|
||||
{
|
||||
bool isView = _innerQuery.IsView;
|
||||
sql.AppendFormat(", ROW_NUMBER() OVER (PARTITION BY {0} {1}) As GroupRow ", BuildBaseTablePKColumns(isView), _innerQuery.OrderBy.BuildQuery(isView));
|
||||
}
|
||||
|
||||
private string BuildBaseTablePKColumns(bool useAltName = true)
|
||||
{
|
||||
Table baseTable = GetBaseTable();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var col in baseTable.Columns.PrimaryKeys)
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
sb.AppendLine(", ");
|
||||
|
||||
string columnName = useAltName ?
|
||||
_innerQuery.NameOrAltName(col.ColumnInfo) :
|
||||
col.ColumnInfo.Name;
|
||||
|
||||
sb.AppendFormat(_innerQuery.Dialect.CreateToken(string.Concat(baseTable.Alias, ".", columnName)));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void BuildRowNumberColumn(StringBuilder sql)
|
||||
{
|
||||
string orderBy = _innerQuery.OrderBy.ToString();
|
||||
// Remove table prefixes from order columns
|
||||
foreach (Table t in _innerQuery.Tables)
|
||||
{
|
||||
orderBy = orderBy.Replace(string.Format("[{0}].", t.Alias), "");
|
||||
}
|
||||
|
||||
sql.AppendFormat(", ROW_NUMBER() OVER ({0}) As RowNumber ", orderBy);
|
||||
}
|
||||
|
||||
private Table GetBaseTable()
|
||||
{
|
||||
Table baseTable = null;
|
||||
if (_innerQuery.Tables[0] is View)
|
||||
{
|
||||
baseTable = (_innerQuery.Tables[0] as View).Tables[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
baseTable = _innerQuery.Tables[0];
|
||||
}
|
||||
return baseTable;
|
||||
}
|
||||
|
||||
public void BuildSelectClause(StringBuilder sql)
|
||||
{
|
||||
List<string> appended = new List<string>();
|
||||
|
||||
sql.Append("SELECT ");
|
||||
|
||||
int startIndex = sql.Length;
|
||||
|
||||
// COLUMNS
|
||||
foreach (Table join in _innerQuery.Tables)
|
||||
{
|
||||
for (int i = 0; i < join.Columns.Count; i++)
|
||||
{
|
||||
var c = join.Columns[i];
|
||||
|
||||
if (sql.Length > startIndex && sql[sql.Length - 1] != ',')
|
||||
sql.Append(",");
|
||||
|
||||
if (join is View)
|
||||
{
|
||||
string token = _innerQuery.Dialect.CreateToken(string.Concat(join.Alias, ".", _innerQuery.NameOrAltName(c.ColumnInfo)));
|
||||
if (appended.Contains(token))
|
||||
continue;
|
||||
|
||||
sql.Append(token);
|
||||
appended.Add(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
string token = string.Concat(join.Alias, ".", c.ColumnInfo.Name);
|
||||
if (appended.Contains(token))
|
||||
continue;
|
||||
|
||||
sql.Append(_innerQuery.Dialect.CreateToken(token));
|
||||
|
||||
if (_innerQuery.UseAltName && c.ColumnInfo.AltName != null && c.ColumnInfo.AltName != c.ColumnInfo.Name)
|
||||
{
|
||||
string altName = c.ColumnInfo.AltName;
|
||||
sql.AppendFormat(" AS {0}", altName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
532
Marr.Data/QGen/QueryBuilder.cs
Normal file
532
Marr.Data/QGen/QueryBuilder.cs
Normal file
@@ -0,0 +1,532 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.QGen;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Marr.Data.Mapping;
|
||||
using System.Data.Common;
|
||||
using System.Collections;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is responsible for building a select query.
|
||||
/// It uses chaining methods to provide a fluent interface for creating select queries.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class QueryBuilder<T> : ExpressionVisitor, IEnumerable<T>, IQueryBuilder
|
||||
{
|
||||
#region - Private Members -
|
||||
|
||||
private Dialects.Dialect _dialect;
|
||||
private DataMapper _db;
|
||||
private TableCollection _tables;
|
||||
private WhereBuilder<T> _whereBuilder;
|
||||
private SortBuilder<T> _sortBuilder;
|
||||
private bool _useAltName = false;
|
||||
private bool _isGraph = false;
|
||||
private string _queryText;
|
||||
private List<MemberInfo> _childrenToLoad;
|
||||
private bool _enablePaging = false;
|
||||
private int _skip;
|
||||
private int _take;
|
||||
private SortBuilder<T> SortBuilder
|
||||
{
|
||||
get
|
||||
{
|
||||
// Lazy load
|
||||
if (_sortBuilder == null)
|
||||
_sortBuilder = new SortBuilder<T>(this, _db, _whereBuilder, _dialect, _tables, _useAltName);
|
||||
|
||||
return _sortBuilder;
|
||||
}
|
||||
}
|
||||
private List<T> _results = new List<T>();
|
||||
private EntityGraph _entityGraph;
|
||||
private EntityGraph EntGraph
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_entityGraph == null)
|
||||
{
|
||||
_entityGraph = new EntityGraph(typeof(T), _results);
|
||||
}
|
||||
|
||||
return _entityGraph;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region - Constructor -
|
||||
|
||||
public QueryBuilder()
|
||||
{
|
||||
// Used only for unit testing with mock frameworks
|
||||
}
|
||||
|
||||
public QueryBuilder(DataMapper db, Dialects.Dialect dialect)
|
||||
{
|
||||
_db = db;
|
||||
_dialect = dialect;
|
||||
_tables = new TableCollection();
|
||||
_tables.Add(new Table(typeof(T)));
|
||||
_childrenToLoad = new List<MemberInfo>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region - Fluent Methods -
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the base table name that will be used in the query.
|
||||
/// </summary>
|
||||
[Obsolete("This method is obsolete. Use either the FromTable or FromView method instead.", true)]
|
||||
public virtual QueryBuilder<T> From(string tableName)
|
||||
{
|
||||
return FromView(tableName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the base view name that will be used in the query.
|
||||
/// Will try to use the mapped "AltName" values when loading the columns.
|
||||
/// </summary>
|
||||
public virtual QueryBuilder<T> FromView(string viewName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(viewName))
|
||||
throw new ArgumentNullException("view");
|
||||
|
||||
_useAltName = true;
|
||||
|
||||
// Replace the base table with a view with tables
|
||||
View view = new View(viewName, _tables.ToArray());
|
||||
_tables.ReplaceBaseTable(view);
|
||||
|
||||
//// Override the base table name
|
||||
//_tables[0].Name = view;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the base table name that will be used in the query.
|
||||
/// Will not try to use the mapped "AltName" values when loading the columns.
|
||||
/// </summary>
|
||||
public virtual QueryBuilder<T> FromTable(string table)
|
||||
{
|
||||
if (string.IsNullOrEmpty(table))
|
||||
throw new ArgumentNullException("view");
|
||||
|
||||
_useAltName = false;
|
||||
|
||||
// Override the base table name
|
||||
_tables[0].Name = table;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to manually specify the query text.
|
||||
/// </summary>
|
||||
public virtual QueryBuilder<T> QueryText(string queryText)
|
||||
{
|
||||
_queryText = queryText;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If no parameters are passed in, this method instructs the DataMapper to load all related entities in the graph.
|
||||
/// If specific entities are passed in, only these relationships will be loaded.
|
||||
/// </summary>
|
||||
/// <param name="childrenToLoad">A list of related child entites to load (passed in as properties / lambda expressions).</param>
|
||||
public virtual QueryBuilder<T> Graph(params Expression<Func<T, object>>[] childrenToLoad)
|
||||
{
|
||||
TableCollection tablesInView = new TableCollection();
|
||||
if (childrenToLoad.Length > 0)
|
||||
{
|
||||
// Add base table
|
||||
tablesInView.Add(_tables[0]);
|
||||
|
||||
foreach (var exp in childrenToLoad)
|
||||
{
|
||||
MemberInfo child = (exp.Body as MemberExpression).Member;
|
||||
|
||||
var node = EntGraph.Where(g => g.Member != null && g.Member.EqualsMember(child)).FirstOrDefault();
|
||||
if (node != null)
|
||||
{
|
||||
tablesInView.Add(new Table(node.EntityType, JoinType.None));
|
||||
}
|
||||
|
||||
if (!_childrenToLoad.ContainsMember(child))
|
||||
{
|
||||
_childrenToLoad.Add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add all tables in the graph
|
||||
foreach (var node in EntGraph)
|
||||
{
|
||||
tablesInView.Add(new Table(node.EntityType, JoinType.None));
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the base table with a view with tables
|
||||
View view = new View(_tables[0].Name, tablesInView.ToArray());
|
||||
_tables.ReplaceBaseTable(view);
|
||||
|
||||
_isGraph = true;
|
||||
_useAltName = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual QueryBuilder<T> Page(int pageNumber, int pageSize)
|
||||
{
|
||||
_enablePaging = true;
|
||||
_skip = (pageNumber - 1) * pageSize;
|
||||
_take = pageSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
private string[] ParseChildrenToLoad(Expression<Func<T, object>>[] childrenToLoad)
|
||||
{
|
||||
List<string> entitiesToLoad = new List<string>();
|
||||
|
||||
// Parse relationship member names from expression array
|
||||
foreach (var exp in childrenToLoad)
|
||||
{
|
||||
MemberInfo member = (exp.Body as MemberExpression).Member;
|
||||
entitiesToLoad.Add(member.Name);
|
||||
|
||||
}
|
||||
|
||||
return entitiesToLoad.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to interact with the DbDataReader to manually load entities.
|
||||
/// </summary>
|
||||
/// <param name="readerAction">An action that takes a DbDataReader.</param>
|
||||
public virtual void DataReader(Action<DbDataReader> readerAction)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_queryText))
|
||||
throw new ArgumentNullException("The query text cannot be blank.");
|
||||
|
||||
var mappingHelper = new MappingHelper(_db);
|
||||
_db.Command.CommandText = _queryText;
|
||||
|
||||
try
|
||||
{
|
||||
_db.OpenConnection();
|
||||
using (DbDataReader reader = _db.Command.ExecuteReader())
|
||||
{
|
||||
readerAction.Invoke(reader);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_db.CloseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int GetRowCount()
|
||||
{
|
||||
SqlModes previousSqlMode = _db.SqlMode;
|
||||
|
||||
// Generate a row count query
|
||||
string where = _whereBuilder != null ? _whereBuilder.ToString() : string.Empty;
|
||||
|
||||
IQuery query = QueryFactory.CreateRowCountSelectQuery(_tables, _db, where, SortBuilder, _useAltName);
|
||||
string queryText = query.Generate();
|
||||
|
||||
_db.SqlMode = SqlModes.Text;
|
||||
int count = (int)_db.ExecuteScalar(queryText);
|
||||
|
||||
_db.SqlMode = previousSqlMode;
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the query and returns a list of results.
|
||||
/// </summary>
|
||||
/// <returns>A list of query results of type T.</returns>
|
||||
public virtual List<T> ToList()
|
||||
{
|
||||
SqlModes previousSqlMode = _db.SqlMode;
|
||||
|
||||
BuildQueryOrAppendClauses();
|
||||
|
||||
if (_isGraph)
|
||||
{
|
||||
_results = (List<T>)_db.QueryToGraph<T>(_queryText, EntGraph, _childrenToLoad);
|
||||
}
|
||||
else
|
||||
{
|
||||
_results = (List<T>)_db.Query<T>(_queryText, _results, _useAltName);
|
||||
}
|
||||
|
||||
// Return to previous sql mode
|
||||
_db.SqlMode = previousSqlMode;
|
||||
|
||||
return _results;
|
||||
}
|
||||
|
||||
private void BuildQueryOrAppendClauses()
|
||||
{
|
||||
if (_queryText == null)
|
||||
{
|
||||
// Build entire query
|
||||
_db.SqlMode = SqlModes.Text;
|
||||
BuildQuery();
|
||||
}
|
||||
else if (_whereBuilder != null || _sortBuilder != null)
|
||||
{
|
||||
_db.SqlMode = SqlModes.Text;
|
||||
if (_whereBuilder != null)
|
||||
{
|
||||
// Append a where clause to an existing query
|
||||
_queryText = string.Concat(_queryText, " ", _whereBuilder.ToString());
|
||||
}
|
||||
|
||||
if (_sortBuilder != null)
|
||||
{
|
||||
// Append an order clause to an existing query
|
||||
_queryText = string.Concat(_queryText, " ", _sortBuilder.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string BuildQuery()
|
||||
{
|
||||
// Generate a query
|
||||
string where = _whereBuilder != null ? _whereBuilder.ToString() : string.Empty;
|
||||
|
||||
IQuery query = null;
|
||||
if (_enablePaging)
|
||||
{
|
||||
query = QueryFactory.CreatePagingSelectQuery(_tables, _db, where, SortBuilder, _useAltName, _skip, _take);
|
||||
}
|
||||
else
|
||||
{
|
||||
query = QueryFactory.CreateSelectQuery(_tables, _db, where, SortBuilder, _useAltName);
|
||||
}
|
||||
|
||||
_queryText = query.Generate();
|
||||
|
||||
return _queryText;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region - Helper Methods -
|
||||
|
||||
private ColumnMapCollection GetColumns(IEnumerable<string> entitiesToLoad)
|
||||
{
|
||||
// If QueryToGraph<T> and no child load entities are specified, load all children
|
||||
bool loadAllChildren = _useAltName && entitiesToLoad == null;
|
||||
|
||||
// If Query<T>
|
||||
if (!_useAltName)
|
||||
{
|
||||
return MapRepository.Instance.GetColumns(typeof(T));
|
||||
}
|
||||
|
||||
ColumnMapCollection columns = new ColumnMapCollection();
|
||||
|
||||
Type baseEntityType = typeof(T);
|
||||
EntityGraph graph = new EntityGraph(baseEntityType, null);
|
||||
|
||||
foreach (var lvl in graph)
|
||||
{
|
||||
if (loadAllChildren || lvl.IsRoot || entitiesToLoad.Contains(lvl.Member.Name))
|
||||
{
|
||||
columns.AddRange(lvl.Columns);
|
||||
}
|
||||
}
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
public static implicit operator List<T>(QueryBuilder<T> builder)
|
||||
{
|
||||
return builder.ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region - Linq Support -
|
||||
|
||||
public virtual SortBuilder<T> Where<TObj>(Expression<Func<TObj, bool>> filterExpression)
|
||||
{
|
||||
_whereBuilder = new WhereBuilder<T>(_db.Command, _dialect, filterExpression, _tables, _useAltName, true);
|
||||
return SortBuilder;
|
||||
}
|
||||
|
||||
public virtual SortBuilder<T> Where(Expression<Func<T, bool>> filterExpression)
|
||||
{
|
||||
_whereBuilder = new WhereBuilder<T>(_db.Command, _dialect, filterExpression, _tables, false, true);
|
||||
return SortBuilder;
|
||||
}
|
||||
|
||||
public virtual SortBuilder<T> Where(string whereClause)
|
||||
{
|
||||
if (string.IsNullOrEmpty(whereClause))
|
||||
throw new ArgumentNullException("whereClause");
|
||||
|
||||
if (!whereClause.ToUpper().Contains("WHERE "))
|
||||
{
|
||||
whereClause = whereClause.Insert(0, " WHERE ");
|
||||
}
|
||||
|
||||
_whereBuilder = new WhereBuilder<T>(whereClause, _useAltName);
|
||||
return SortBuilder;
|
||||
}
|
||||
|
||||
public virtual SortBuilder<T> OrderBy(Expression<Func<T, object>> sortExpression)
|
||||
{
|
||||
SortBuilder.OrderBy(sortExpression);
|
||||
return SortBuilder;
|
||||
}
|
||||
|
||||
public virtual SortBuilder<T> ThenBy(Expression<Func<T, object>> sortExpression)
|
||||
{
|
||||
SortBuilder.OrderBy(sortExpression);
|
||||
return SortBuilder;
|
||||
}
|
||||
|
||||
public virtual SortBuilder<T> OrderByDescending(Expression<Func<T, object>> sortExpression)
|
||||
{
|
||||
SortBuilder.OrderByDescending(sortExpression);
|
||||
return SortBuilder;
|
||||
}
|
||||
|
||||
public virtual SortBuilder<T> ThenByDescending(Expression<Func<T, object>> sortExpression)
|
||||
{
|
||||
SortBuilder.OrderByDescending(sortExpression);
|
||||
return SortBuilder;
|
||||
}
|
||||
|
||||
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 ");
|
||||
}
|
||||
|
||||
SortBuilder.OrderBy(orderByClause);
|
||||
return SortBuilder;
|
||||
}
|
||||
|
||||
public virtual QueryBuilder<T> Take(int count)
|
||||
{
|
||||
_enablePaging = true;
|
||||
_take = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual QueryBuilder<T> Skip(int count)
|
||||
{
|
||||
_enablePaging = true;
|
||||
_skip = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles all.
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
protected override System.Linq.Expressions.Expression Visit(System.Linq.Expressions.Expression expression)
|
||||
{
|
||||
return base.Visit(expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles Where.
|
||||
/// </summary>
|
||||
/// <param name="lambdaExpression"></param>
|
||||
/// <returns></returns>
|
||||
protected override System.Linq.Expressions.Expression VisitLamda(System.Linq.Expressions.LambdaExpression lambdaExpression)
|
||||
{
|
||||
_sortBuilder = this.Where(lambdaExpression as Expression<Func<T, bool>>);
|
||||
return base.VisitLamda(lambdaExpression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles OrderBy.
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
protected override System.Linq.Expressions.Expression VisitMethodCall(MethodCallExpression expression)
|
||||
{
|
||||
if (expression.Method.Name == "OrderBy" || expression.Method.Name == "ThenBy")
|
||||
{
|
||||
var memberExp = ((expression.Arguments[1] as UnaryExpression).Operand as System.Linq.Expressions.LambdaExpression).Body as System.Linq.Expressions.MemberExpression;
|
||||
_sortBuilder.Order(memberExp.Expression.Type, memberExp.Member.Name);
|
||||
}
|
||||
if (expression.Method.Name == "OrderByDescending" || expression.Method.Name == "ThenByDescending")
|
||||
{
|
||||
var memberExp = ((expression.Arguments[1] as UnaryExpression).Operand as System.Linq.Expressions.LambdaExpression).Body as System.Linq.Expressions.MemberExpression;
|
||||
_sortBuilder.OrderByDescending(memberExp.Expression.Type, memberExp.Member.Name);
|
||||
}
|
||||
|
||||
return base.VisitMethodCall(expression);
|
||||
}
|
||||
|
||||
public virtual QueryBuilder<T> Join<TLeft, TRight>(JoinType joinType, Expression<Func<TLeft, IEnumerable<TRight>>> rightEntity, Expression<Func<TLeft, TRight, bool>> filterExpression)
|
||||
{
|
||||
MemberInfo rightMember = (rightEntity.Body as MemberExpression).Member;
|
||||
return this.Join(joinType, rightMember, filterExpression);
|
||||
}
|
||||
|
||||
public virtual QueryBuilder<T> Join<TLeft, TRight>(JoinType joinType, Expression<Func<TLeft, TRight>> rightEntity, Expression<Func<TLeft, TRight, bool>> filterExpression)
|
||||
{
|
||||
MemberInfo rightMember = (rightEntity.Body as MemberExpression).Member;
|
||||
return this.Join(joinType, rightMember, filterExpression);
|
||||
}
|
||||
|
||||
public virtual QueryBuilder<T> Join<TLeft, TRight>(JoinType joinType, MemberInfo rightMember, Expression<Func<TLeft, TRight, bool>> filterExpression)
|
||||
{
|
||||
_useAltName = true;
|
||||
_isGraph = true;
|
||||
|
||||
if (!_childrenToLoad.ContainsMember(rightMember))
|
||||
_childrenToLoad.Add(rightMember);
|
||||
|
||||
Table table = new Table(typeof(TRight), joinType);
|
||||
_tables.Add(table);
|
||||
|
||||
var builder = new JoinBuilder<TLeft, TRight>(_db.Command, _dialect, filterExpression, _tables);
|
||||
|
||||
table.JoinClause = builder.ToString();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<T> Members
|
||||
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||
{
|
||||
var list = this.ToList();
|
||||
return list.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
var list = this.ToList();
|
||||
return list.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
110
Marr.Data/QGen/QueryFactory.cs
Normal file
110
Marr.Data/QGen/QueryFactory.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Data.Common;
|
||||
using Marr.Data.QGen.Dialects;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class contains the factory logic that determines which type of IQuery object should be created.
|
||||
/// </summary>
|
||||
internal class QueryFactory
|
||||
{
|
||||
private const string DB_SqlClient = "System.Data.SqlClient.SqlClientFactory";
|
||||
private const string DB_OleDb = "System.Data.OleDb.OleDbFactory";
|
||||
private const string DB_SqlCeClient = "System.Data.SqlServerCe.SqlCeProviderFactory";
|
||||
private const string DB_SystemDataOracleClient = "System.Data.OracleClientFactory";
|
||||
private const string DB_OracleDataAccessClient = "Oracle.DataAccess.Client.OracleClientFactory";
|
||||
private const string DB_FireBirdClient = "FirebirdSql.Data.FirebirdClient.FirebirdClientFactory";
|
||||
private const string DB_SQLiteClient = "System.Data.SQLite.SQLiteFactory";
|
||||
|
||||
public static IQuery CreateUpdateQuery(Mapping.ColumnMapCollection columns, IDataMapper dataMapper, string target, string whereClause)
|
||||
{
|
||||
Dialect dialect = CreateDialect(dataMapper);
|
||||
return new UpdateQuery(dialect, columns, dataMapper.Command, target, whereClause);
|
||||
}
|
||||
|
||||
public static IQuery CreateInsertQuery(Mapping.ColumnMapCollection columns, IDataMapper dataMapper, string target)
|
||||
{
|
||||
Dialect dialect = CreateDialect(dataMapper);
|
||||
return new InsertQuery(dialect, columns, dataMapper.Command, target);
|
||||
}
|
||||
|
||||
public static IQuery CreateDeleteQuery(Dialects.Dialect dialect, Table targetTable, string whereClause)
|
||||
{
|
||||
return new DeleteQuery(dialect, targetTable, whereClause);
|
||||
}
|
||||
|
||||
public static IQuery CreateSelectQuery(TableCollection tables, IDataMapper dataMapper, string where, ISortQueryBuilder orderBy, bool useAltName)
|
||||
{
|
||||
Dialect dialect = CreateDialect(dataMapper);
|
||||
return new SelectQuery(dialect, tables, where, orderBy, useAltName);
|
||||
}
|
||||
|
||||
public static IQuery CreateRowCountSelectQuery(TableCollection tables, IDataMapper dataMapper, string where, ISortQueryBuilder orderBy, bool useAltName)
|
||||
{
|
||||
SelectQuery innerQuery = (SelectQuery)CreateSelectQuery(tables, dataMapper, where, orderBy, useAltName);
|
||||
|
||||
string providerString = dataMapper.ProviderFactory.ToString();
|
||||
switch (providerString)
|
||||
{
|
||||
case DB_SqlClient:
|
||||
return new RowCountQueryDecorator(innerQuery);
|
||||
|
||||
case DB_SqlCeClient:
|
||||
return new RowCountQueryDecorator(innerQuery);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("Row count has not yet been implemented for this provider.");
|
||||
}
|
||||
}
|
||||
|
||||
public static IQuery CreatePagingSelectQuery(TableCollection tables, IDataMapper dataMapper, string where, ISortQueryBuilder orderBy, bool useAltName, int skip, int take)
|
||||
{
|
||||
SelectQuery innerQuery = (SelectQuery)CreateSelectQuery(tables, dataMapper, where, orderBy, useAltName);
|
||||
|
||||
string providerString = dataMapper.ProviderFactory.ToString();
|
||||
switch (providerString)
|
||||
{
|
||||
case DB_SqlClient:
|
||||
return new PagingQueryDecorator(innerQuery, skip, take);
|
||||
|
||||
case DB_SqlCeClient:
|
||||
return new PagingQueryDecorator(innerQuery, skip, take);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("Paging has not yet been implemented for this provider.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Dialects.Dialect CreateDialect(IDataMapper dataMapper)
|
||||
{
|
||||
string providerString = dataMapper.ProviderFactory.ToString();
|
||||
switch (providerString)
|
||||
{
|
||||
case DB_SqlClient:
|
||||
return new SqlServerDialect();
|
||||
|
||||
case DB_OracleDataAccessClient:
|
||||
return new OracleDialect();
|
||||
|
||||
case DB_SystemDataOracleClient:
|
||||
return new OracleDialect();
|
||||
|
||||
case DB_SqlCeClient:
|
||||
return new SqlServerCeDialect();
|
||||
|
||||
case DB_FireBirdClient:
|
||||
return new FirebirdDialect();
|
||||
|
||||
case DB_SQLiteClient:
|
||||
return new SqliteDialect();
|
||||
|
||||
default:
|
||||
return new Dialect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Marr.Data/QGen/QueryQueueItem.cs
Normal file
19
Marr.Data/QGen/QueryQueueItem.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
|
||||
//namespace Marr.Data.QGen
|
||||
//{
|
||||
// public class QueryQueueItem
|
||||
// {
|
||||
// public QueryQueueItem(string queryText, IEnumerable<string> entitiesToLoad)
|
||||
// {
|
||||
// QueryText = queryText;
|
||||
// EntitiesToLoad = entitiesToLoad;
|
||||
// }
|
||||
|
||||
// public string QueryText { get; set; }
|
||||
// public IEnumerable<string> EntitiesToLoad { get; private set; }
|
||||
// }
|
||||
//}
|
||||
127
Marr.Data/QGen/RowCountQueryDecorator.cs
Normal file
127
Marr.Data/QGen/RowCountQueryDecorator.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
public class RowCountQueryDecorator : IQuery
|
||||
{
|
||||
private SelectQuery _innerQuery;
|
||||
|
||||
public RowCountQueryDecorator(SelectQuery innerQuery)
|
||||
{
|
||||
_innerQuery = innerQuery;
|
||||
}
|
||||
|
||||
public string Generate()
|
||||
{
|
||||
// Decide which type of paging query to create
|
||||
if (_innerQuery.IsView || _innerQuery.IsJoin)
|
||||
{
|
||||
return ComplexRowCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
return SimpleRowCount();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a row count query for a multiple table joined query (groups by the parent entity).
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string ComplexRowCount()
|
||||
{
|
||||
// Create paged query
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
sql.AppendLine("WITH GroupCTE AS (");
|
||||
sql.Append("SELECT ").AppendLine(BuildBaseTablePKColumns());
|
||||
BuildGroupColumn(sql);
|
||||
_innerQuery.BuildFromClause(sql);
|
||||
_innerQuery.BuildJoinClauses(sql);
|
||||
_innerQuery.BuildWhereClause(sql);
|
||||
sql.AppendLine(")");
|
||||
BuildSelectCountClause(sql);
|
||||
sql.AppendLine("FROM GroupCTE");
|
||||
sql.AppendLine("WHERE GroupRow = 1");
|
||||
|
||||
return sql.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a row count query for a single table query (no joins).
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string SimpleRowCount()
|
||||
{
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
BuildSelectCountClause(sql);
|
||||
_innerQuery.BuildFromClause(sql);
|
||||
_innerQuery.BuildJoinClauses(sql);
|
||||
_innerQuery.BuildWhereClause(sql);
|
||||
|
||||
return sql.ToString();
|
||||
}
|
||||
|
||||
private void BuildGroupColumn(StringBuilder sql)
|
||||
{
|
||||
string baseTablePKColumns = BuildBaseTablePKColumns();
|
||||
sql.AppendFormat(", ROW_NUMBER() OVER (PARTITION BY {0} ORDER BY {1}) As GroupRow ", baseTablePKColumns, baseTablePKColumns);
|
||||
}
|
||||
|
||||
private string BuildBaseTablePKColumns()
|
||||
{
|
||||
Table baseTable = GetBaseTable();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var col in baseTable.Columns.PrimaryKeys)
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
sb.AppendLine(", ");
|
||||
|
||||
string colName = _innerQuery.IsView ?
|
||||
_innerQuery.NameOrAltName(col.ColumnInfo) :
|
||||
col.ColumnInfo.Name;
|
||||
|
||||
sb.AppendFormat(_innerQuery.Dialect.CreateToken(string.Concat(baseTable.Alias, ".", colName)));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void BuildSelectCountClause(StringBuilder sql)
|
||||
{
|
||||
sql.AppendLine("SELECT COUNT(*)");
|
||||
}
|
||||
|
||||
private Table GetBaseTable()
|
||||
{
|
||||
Table baseTable = null;
|
||||
if (_innerQuery.Tables[0] is View)
|
||||
{
|
||||
baseTable = (_innerQuery.Tables[0] as View).Tables[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
baseTable = _innerQuery.Tables[0];
|
||||
}
|
||||
return baseTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
WITH GroupCTE AS
|
||||
(
|
||||
SELECT [t0].[ID],[t0].[OrderName],[t1].[ID] AS OrderItemID,[t1].[OrderID],[t1].[ItemDescription],[t1].[Price],
|
||||
ROW_NUMBER() OVER (PARTITION BY [t0].[ID] ORDER BY [t0].[OrderName]) As GroupRow
|
||||
FROM [Order] [t0]
|
||||
LEFT JOIN [OrderItem] [t1] ON (([t0].[ID] = [t1].[OrderID]))
|
||||
--WHERE (([t0].[OrderName] = @P0))
|
||||
)
|
||||
SELECT * FROM GroupCTE
|
||||
WHERE GroupRow = 1
|
||||
*/
|
||||
157
Marr.Data/QGen/SelectQuery.cs
Normal file
157
Marr.Data/QGen/SelectQuery.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data;
|
||||
using Marr.Data.Mapping;
|
||||
using System.Data.Common;
|
||||
using Marr.Data.QGen.Dialects;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is responsible for creating a select query.
|
||||
/// </summary>
|
||||
public class SelectQuery : IQuery
|
||||
{
|
||||
public Dialect Dialect { get; set; }
|
||||
public string WhereClause { get; set; }
|
||||
public ISortQueryBuilder OrderBy { get; set; }
|
||||
public TableCollection Tables { get; set; }
|
||||
public bool UseAltName;
|
||||
|
||||
public SelectQuery(Dialect dialect, TableCollection tables, string whereClause, ISortQueryBuilder orderBy, bool useAltName)
|
||||
{
|
||||
Dialect = dialect;
|
||||
Tables = tables;
|
||||
WhereClause = whereClause;
|
||||
OrderBy = orderBy;
|
||||
UseAltName = useAltName;
|
||||
}
|
||||
|
||||
public bool IsView
|
||||
{
|
||||
get
|
||||
{
|
||||
return Tables[0] is View;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsJoin
|
||||
{
|
||||
get
|
||||
{
|
||||
return Tables.Count > 1;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Generate()
|
||||
{
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
BuildSelectClause(sql);
|
||||
BuildFromClause(sql);
|
||||
BuildJoinClauses(sql);
|
||||
BuildWhereClause(sql);
|
||||
BuildOrderClause(sql);
|
||||
|
||||
return sql.ToString();
|
||||
}
|
||||
|
||||
public void BuildSelectClause(StringBuilder sql)
|
||||
{
|
||||
sql.Append("SELECT ");
|
||||
|
||||
int startIndex = sql.Length;
|
||||
|
||||
// COLUMNS
|
||||
foreach (Table join in Tables)
|
||||
{
|
||||
for (int i = 0; i < join.Columns.Count; i++)
|
||||
{
|
||||
var c = join.Columns[i];
|
||||
|
||||
if (sql.Length > startIndex)
|
||||
sql.Append(",");
|
||||
|
||||
if (join is View)
|
||||
{
|
||||
string token = string.Concat(join.Alias, ".", NameOrAltName(c.ColumnInfo));
|
||||
sql.Append(Dialect.CreateToken(token));
|
||||
}
|
||||
else
|
||||
{
|
||||
string token = string.Concat(join.Alias, ".", c.ColumnInfo.Name);
|
||||
sql.Append(Dialect.CreateToken(token));
|
||||
|
||||
if (UseAltName && c.ColumnInfo.AltName != null && c.ColumnInfo.AltName != c.ColumnInfo.Name)
|
||||
{
|
||||
string altName = c.ColumnInfo.AltName;
|
||||
sql.AppendFormat(" AS {0}", altName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string NameOrAltName(IColumnInfo columnInfo)
|
||||
{
|
||||
if (UseAltName && columnInfo.AltName != null && columnInfo.AltName != columnInfo.Name)
|
||||
{
|
||||
return columnInfo.AltName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return columnInfo.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public void BuildFromClause(StringBuilder sql)
|
||||
{
|
||||
// BASE TABLE
|
||||
Table baseTable = Tables[0];
|
||||
sql.AppendFormat(" FROM {0} {1} ", Dialect.CreateToken(baseTable.Name), Dialect.CreateToken(baseTable.Alias));
|
||||
}
|
||||
|
||||
public void BuildJoinClauses(StringBuilder sql)
|
||||
{
|
||||
// JOINS
|
||||
for (int i = 1; i < Tables.Count; i++)
|
||||
{
|
||||
if (Tables[i].JoinType != JoinType.None)
|
||||
{
|
||||
sql.AppendFormat("{0} {1} {2} {3} ",
|
||||
TranslateJoin(Tables[i].JoinType),
|
||||
Dialect.CreateToken(Tables[i].Name),
|
||||
Dialect.CreateToken(Tables[i].Alias),
|
||||
Tables[i].JoinClause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BuildWhereClause(StringBuilder sql)
|
||||
{
|
||||
sql.Append(WhereClause);
|
||||
}
|
||||
|
||||
public void BuildOrderClause(StringBuilder sql)
|
||||
{
|
||||
sql.Append(OrderBy.ToString());
|
||||
}
|
||||
|
||||
private string TranslateJoin(JoinType join)
|
||||
{
|
||||
switch (join)
|
||||
{
|
||||
case JoinType.Inner:
|
||||
return "INNER JOIN";
|
||||
case JoinType.Left:
|
||||
return "LEFT JOIN";
|
||||
case JoinType.Right:
|
||||
return "RIGHT JOIN";
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
246
Marr.Data/QGen/SortBuilder.cs
Normal file
246
Marr.Data/QGen/SortBuilder.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
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, _useAltName, true);
|
||||
_whereBuilder.Append(orWhere, WhereAppendType.OR);
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual SortBuilder<T> OrWhere(string whereClause)
|
||||
{
|
||||
var orWhere = new WhereBuilder<T>(whereClause, _useAltName);
|
||||
_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, _useAltName, true);
|
||||
_whereBuilder.Append(andWhere, WhereAppendType.AND);
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual SortBuilder<T> AndWhere(string whereClause)
|
||||
{
|
||||
var andWhere = new WhereBuilder<T>(whereClause, _useAltName);
|
||||
_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> 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> 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);
|
||||
sb.Append(_dialect.CreateToken(string.Format("{0}.{1}", table.Alias, 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 = this.ToList();
|
||||
return list.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
50
Marr.Data/QGen/SortColumn.cs
Normal file
50
Marr.Data/QGen/SortColumn.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
public class SortColumn<T>
|
||||
{
|
||||
public SortColumn(Expression<Func<T, object>> sortExpression, SortDirection direction)
|
||||
{
|
||||
MemberExpression me = GetMemberExpression(sortExpression.Body);
|
||||
DeclaringType = me.Expression.Type;
|
||||
PropertyName = me.Member.Name;
|
||||
Direction = direction;
|
||||
}
|
||||
|
||||
public SortColumn(Type declaringType, string propertyName, SortDirection direction)
|
||||
{
|
||||
DeclaringType = declaringType;
|
||||
PropertyName = propertyName;
|
||||
Direction = direction;
|
||||
}
|
||||
|
||||
public SortDirection Direction { get; private set; }
|
||||
public Type DeclaringType { get; private set; }
|
||||
public string PropertyName { get; private set; }
|
||||
|
||||
private MemberExpression GetMemberExpression(Expression exp)
|
||||
{
|
||||
MemberExpression me = exp as MemberExpression;
|
||||
|
||||
if (me == null)
|
||||
{
|
||||
var ue = exp as UnaryExpression;
|
||||
me = ue.Operand as MemberExpression;
|
||||
}
|
||||
|
||||
return me;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SortDirection
|
||||
{
|
||||
Asc,
|
||||
Desc
|
||||
}
|
||||
}
|
||||
50
Marr.Data/QGen/Table.cs
Normal file
50
Marr.Data/QGen/Table.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.Mapping;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents a table in a query.
|
||||
/// A table contains corresponding columns.
|
||||
/// </summary>
|
||||
public class Table
|
||||
{
|
||||
public Table(Type memberType)
|
||||
: this(memberType, JoinType.None)
|
||||
{ }
|
||||
|
||||
public Table(Type memberType, JoinType joinType)
|
||||
{
|
||||
EntityType = memberType;
|
||||
Name = memberType.GetTableName();
|
||||
JoinType = joinType;
|
||||
Columns = MapRepository.Instance.GetColumns(memberType);
|
||||
}
|
||||
|
||||
public bool IsBaseTable
|
||||
{
|
||||
get
|
||||
{
|
||||
return Alias == "t0";
|
||||
}
|
||||
}
|
||||
|
||||
public Type EntityType { get; private set; }
|
||||
public virtual string Name { get; set; }
|
||||
public JoinType JoinType { get; private set; }
|
||||
public virtual ColumnMapCollection Columns { get; private set; }
|
||||
public virtual string Alias { get; set; }
|
||||
public string JoinClause { get; set; }
|
||||
}
|
||||
|
||||
public enum JoinType
|
||||
{
|
||||
None,
|
||||
Inner,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
}
|
||||
97
Marr.Data/QGen/TableCollection.cs
Normal file
97
Marr.Data/QGen/TableCollection.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class holds a collection of Table objects.
|
||||
/// </summary>
|
||||
public class TableCollection : IEnumerable<Table>
|
||||
{
|
||||
private List<Table> _tables;
|
||||
|
||||
public TableCollection()
|
||||
{
|
||||
_tables = new List<Table>();
|
||||
}
|
||||
|
||||
public void Add(Table table)
|
||||
{
|
||||
if (this.Any(t => t.EntityType == table.EntityType))
|
||||
{
|
||||
// Already exists -- don't add
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an alias (ex: "t0", "t1", "t2", etc...)
|
||||
table.Alias = string.Format("t{0}", _tables.Count);
|
||||
_tables.Add(table);
|
||||
}
|
||||
|
||||
public void ReplaceBaseTable(View view)
|
||||
{
|
||||
_tables.RemoveAt(0);
|
||||
Add(view);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a table for a given member.
|
||||
/// </summary>
|
||||
public Table FindTable(Type declaringType)
|
||||
{
|
||||
return this.EnumerateViewsAndTables().Where(t => t.EntityType == declaringType).FirstOrDefault();
|
||||
}
|
||||
|
||||
public Table this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tables[index];
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tables.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively enumerates through all tables, including tables embedded in views.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Table> EnumerateViewsAndTables()
|
||||
{
|
||||
foreach (Table table in _tables)
|
||||
{
|
||||
if (table is View)
|
||||
{
|
||||
foreach (Table viewTable in (table as View))
|
||||
{
|
||||
yield return viewTable;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return table;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Table> GetEnumerator()
|
||||
{
|
||||
return _tables.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _tables.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Marr.Data/QGen/UpdateQuery.cs
Normal file
59
Marr.Data/QGen/UpdateQuery.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Data.Common;
|
||||
using Marr.Data.Mapping;
|
||||
using Marr.Data.QGen.Dialects;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
public class UpdateQuery : IQuery
|
||||
{
|
||||
protected Dialect Dialect { get; set; }
|
||||
protected string Target { get; set; }
|
||||
protected ColumnMapCollection Columns { get; set; }
|
||||
protected DbCommand Command { get; set; }
|
||||
protected string WhereClause { get; set; }
|
||||
|
||||
public UpdateQuery(Dialect dialect, ColumnMapCollection columns, DbCommand command, string target, string whereClause)
|
||||
{
|
||||
Dialect = dialect;
|
||||
Target = target;
|
||||
Columns = columns;
|
||||
Command = command;
|
||||
WhereClause = whereClause;
|
||||
}
|
||||
|
||||
public string Generate()
|
||||
{
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
sql.AppendFormat("UPDATE {0} SET ", Dialect.CreateToken(Target));
|
||||
|
||||
int startIndex = sql.Length;
|
||||
|
||||
foreach (DbParameter p in Command.Parameters)
|
||||
{
|
||||
var c = Columns.GetByColumnName(p.ParameterName);
|
||||
|
||||
if (c == null)
|
||||
break; // All SET columns have been added
|
||||
|
||||
if (sql.Length > startIndex)
|
||||
sql.Append(",");
|
||||
|
||||
if (!c.ColumnInfo.IsAutoIncrement)
|
||||
{
|
||||
sql.AppendFormat("{0}={1}{2}", Dialect.CreateToken(c.ColumnInfo.Name), Command.ParameterPrefix(), p.ParameterName);
|
||||
}
|
||||
}
|
||||
|
||||
sql.AppendFormat(" {0}", WhereClause);
|
||||
|
||||
return sql.ToString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
176
Marr.Data/QGen/UpdateQueryBuilder.cs
Normal file
176
Marr.Data/QGen/UpdateQueryBuilder.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.Mapping;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
public class UpdateQueryBuilder<T>
|
||||
{
|
||||
private DataMapper _db;
|
||||
private string _tableName;
|
||||
private T _entity;
|
||||
private MappingHelper _mappingHelper;
|
||||
private ColumnMapCollection _mappings;
|
||||
private SqlModes _previousSqlMode;
|
||||
private bool _generateQuery = true;
|
||||
private TableCollection _tables;
|
||||
private Expression<Func<T, bool>> _filterExpression;
|
||||
private Dialects.Dialect _dialect;
|
||||
private ColumnMapCollection _columnsToUpdate;
|
||||
|
||||
public UpdateQueryBuilder()
|
||||
{
|
||||
// Used only for unit testing with mock frameworks
|
||||
}
|
||||
|
||||
public UpdateQueryBuilder(DataMapper db)
|
||||
{
|
||||
_db = db;
|
||||
_tableName = MapRepository.Instance.GetTableName(typeof(T));
|
||||
_tables = new TableCollection();
|
||||
_tables.Add(new Table(typeof(T)));
|
||||
_previousSqlMode = _db.SqlMode;
|
||||
_mappingHelper = new MappingHelper(_db);
|
||||
_mappings = MapRepository.Instance.GetColumns(typeof(T));
|
||||
_dialect = QueryFactory.CreateDialect(_db);
|
||||
}
|
||||
|
||||
public virtual UpdateQueryBuilder<T> TableName(string tableName)
|
||||
{
|
||||
_tableName = tableName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual UpdateQueryBuilder<T> QueryText(string queryText)
|
||||
{
|
||||
_generateQuery = false;
|
||||
_db.Command.CommandText = queryText;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual UpdateQueryBuilder<T> Entity(T entity)
|
||||
{
|
||||
_entity = entity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual UpdateQueryBuilder<T> Where(Expression<Func<T, bool>> filterExpression)
|
||||
{
|
||||
_filterExpression = filterExpression;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual UpdateQueryBuilder<T> ColumnsIncluding(params Expression<Func<T, object>>[] properties)
|
||||
{
|
||||
List<string> columnList = new List<string>();
|
||||
|
||||
foreach (var column in properties)
|
||||
{
|
||||
columnList.Add(column.GetMemberName());
|
||||
}
|
||||
|
||||
return ColumnsIncluding(columnList.ToArray());
|
||||
}
|
||||
|
||||
public virtual UpdateQueryBuilder<T> ColumnsIncluding(params string[] properties)
|
||||
{
|
||||
_columnsToUpdate = new ColumnMapCollection();
|
||||
|
||||
foreach (string propertyName in properties)
|
||||
{
|
||||
_columnsToUpdate.Add(_mappings.GetByFieldName(propertyName));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual UpdateQueryBuilder<T> ColumnsExcluding(params Expression<Func<T, object>>[] properties)
|
||||
{
|
||||
List<string> columnList = new List<string>();
|
||||
|
||||
foreach (var column in properties)
|
||||
{
|
||||
columnList.Add(column.GetMemberName());
|
||||
}
|
||||
|
||||
return ColumnsExcluding(columnList.ToArray());
|
||||
}
|
||||
|
||||
public virtual UpdateQueryBuilder<T> ColumnsExcluding(params string[] properties)
|
||||
{
|
||||
_columnsToUpdate = new ColumnMapCollection();
|
||||
|
||||
_columnsToUpdate.AddRange(_mappings);
|
||||
|
||||
foreach (string propertyName in properties)
|
||||
{
|
||||
_columnsToUpdate.RemoveAll(c => c.FieldName == propertyName);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual string BuildQuery()
|
||||
{
|
||||
if (_entity == null)
|
||||
throw new ArgumentNullException("You must specify an entity to update.");
|
||||
|
||||
// Override SqlMode since we know this will be a text query
|
||||
_db.SqlMode = SqlModes.Text;
|
||||
|
||||
var columnsToUpdate = _columnsToUpdate ?? _mappings;
|
||||
|
||||
_mappingHelper.CreateParameters<T>(_entity, columnsToUpdate, _generateQuery);
|
||||
|
||||
string where = string.Empty;
|
||||
if (_filterExpression != null)
|
||||
{
|
||||
var whereBuilder = new WhereBuilder<T>(_db.Command, _dialect, _filterExpression, _tables, false, false);
|
||||
where = whereBuilder.ToString();
|
||||
}
|
||||
|
||||
IQuery query = QueryFactory.CreateUpdateQuery(columnsToUpdate, _db, _tableName, where);
|
||||
|
||||
_db.Command.CommandText = query.Generate();
|
||||
|
||||
return _db.Command.CommandText;
|
||||
}
|
||||
|
||||
public virtual int Execute()
|
||||
{
|
||||
if (_generateQuery)
|
||||
{
|
||||
BuildQuery();
|
||||
}
|
||||
else
|
||||
{
|
||||
_mappingHelper.CreateParameters<T>(_entity, _mappings, _generateQuery);
|
||||
}
|
||||
|
||||
int rowsAffected = 0;
|
||||
|
||||
try
|
||||
{
|
||||
_db.OpenConnection();
|
||||
rowsAffected = _db.Command.ExecuteNonQuery();
|
||||
_mappingHelper.SetOutputValues<T>(_entity, _mappings.OutputFields);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_db.CloseConnection();
|
||||
}
|
||||
|
||||
|
||||
if (_generateQuery)
|
||||
{
|
||||
// Return to previous sql mode
|
||||
_db.SqlMode = _previousSqlMode;
|
||||
}
|
||||
|
||||
return rowsAffected;
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Marr.Data/QGen/View.cs
Normal file
91
Marr.Data/QGen/View.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.Mapping;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents a View. A view can hold multiple tables (and their columns).
|
||||
/// </summary>
|
||||
public class View : Table, IEnumerable<Table>
|
||||
{
|
||||
private string _viewName;
|
||||
private Table[] _tables;
|
||||
private Mapping.ColumnMapCollection _columns;
|
||||
|
||||
public View(string viewName, Table[] tables)
|
||||
: base(tables[0].EntityType, JoinType.None)
|
||||
{
|
||||
_viewName = viewName;
|
||||
_tables = tables;
|
||||
}
|
||||
|
||||
public Table[] Tables
|
||||
{
|
||||
get { return _tables; }
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _viewName;
|
||||
}
|
||||
set
|
||||
{
|
||||
_viewName = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override string Alias
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Alias;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.Alias = value;
|
||||
|
||||
// Sync view tables
|
||||
foreach (Table table in _tables)
|
||||
{
|
||||
table.Alias = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the columns from all the tables included in the view.
|
||||
/// </summary>
|
||||
public override Mapping.ColumnMapCollection Columns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_columns == null)
|
||||
{
|
||||
var allColumns = _tables.SelectMany(t => t.Columns);
|
||||
_columns = new ColumnMapCollection();
|
||||
_columns.AddRange(allColumns);
|
||||
}
|
||||
|
||||
return _columns;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Table> GetEnumerator()
|
||||
{
|
||||
foreach (Table table in _tables)
|
||||
{
|
||||
yield return table;
|
||||
}
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
279
Marr.Data/QGen/WhereBuilder.cs
Normal file
279
Marr.Data/QGen/WhereBuilder.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Linq.Expressions;
|
||||
using Marr.Data;
|
||||
using Marr.Data.Mapping;
|
||||
using System.Data.Common;
|
||||
using Marr.Data.Parameters;
|
||||
using System.Reflection;
|
||||
using Marr.Data.QGen.Dialects;
|
||||
|
||||
namespace Marr.Data.QGen
|
||||
{
|
||||
/// <summary>
|
||||
/// This class utilizes the ExpressionVisitor base class, and it is responsible for creating the "WHERE" clause.
|
||||
/// It builds a protected StringBuilder class whose output is created when the ToString method is called.
|
||||
/// It also has some methods that coincide with Linq methods, to provide Linq compatibility.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class WhereBuilder<T> : ExpressionVisitor
|
||||
{
|
||||
private string _constantWhereClause;
|
||||
private MapRepository _repos;
|
||||
private DbCommand _command;
|
||||
private string _paramPrefix;
|
||||
private bool isLeftSide = true;
|
||||
protected bool _useAltName;
|
||||
protected Dialect _dialect;
|
||||
protected StringBuilder _sb;
|
||||
protected TableCollection _tables;
|
||||
protected bool _tablePrefix;
|
||||
|
||||
public WhereBuilder(string whereClause, bool useAltName)
|
||||
{
|
||||
_constantWhereClause = whereClause;
|
||||
_useAltName = useAltName;
|
||||
}
|
||||
|
||||
public WhereBuilder(DbCommand command, Dialect dialect, Expression filter, TableCollection tables, bool useAltName, bool tablePrefix)
|
||||
{
|
||||
_repos = MapRepository.Instance;
|
||||
_command = command;
|
||||
_dialect = dialect;
|
||||
_paramPrefix = command.ParameterPrefix();
|
||||
_sb = new StringBuilder();
|
||||
_useAltName = useAltName;
|
||||
_tables = tables;
|
||||
_tablePrefix = tablePrefix;
|
||||
|
||||
if (filter != null)
|
||||
{
|
||||
_sb.AppendFormat("{0} ", PrefixText);
|
||||
base.Visit(filter);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual string PrefixText
|
||||
{
|
||||
get
|
||||
{
|
||||
return "WHERE";
|
||||
}
|
||||
}
|
||||
|
||||
protected override Expression VisitBinary(BinaryExpression expression)
|
||||
{
|
||||
_sb.Append("(");
|
||||
|
||||
isLeftSide = true;
|
||||
Visit(expression.Left);
|
||||
|
||||
_sb.AppendFormat(" {0} ", Decode(expression.NodeType));
|
||||
|
||||
isLeftSide = false;
|
||||
Visit(expression.Right);
|
||||
|
||||
_sb.Append(")");
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
protected override Expression VisitMethodCall(MethodCallExpression expression)
|
||||
{
|
||||
string method = (expression as System.Linq.Expressions.MethodCallExpression).Method.Name;
|
||||
switch (method)
|
||||
{
|
||||
case "Contains":
|
||||
Write_Contains(expression);
|
||||
break;
|
||||
|
||||
case "StartsWith":
|
||||
Write_StartsWith(expression);
|
||||
break;
|
||||
|
||||
case "EndsWith":
|
||||
Write_EndsWith(expression);
|
||||
break;
|
||||
|
||||
default:
|
||||
string msg = string.Format("'{0}' expressions are not yet implemented in the where clause expression tree parser.", method);
|
||||
throw new NotImplementedException(msg);
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
protected override Expression VisitMemberAccess(MemberExpression expression)
|
||||
{
|
||||
if (isLeftSide)
|
||||
{
|
||||
string fqColumn = GetFullyQualifiedColumnName(expression.Member, expression.Expression.Type);
|
||||
_sb.Append(fqColumn);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add parameter to Command.Parameters
|
||||
string paramName = string.Concat(_paramPrefix, "P", _command.Parameters.Count.ToString());
|
||||
_sb.Append(paramName);
|
||||
|
||||
object value = GetRightValue(expression);
|
||||
new ParameterChainMethods(_command, paramName, value);
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
protected override Expression VisitConstant(ConstantExpression expression)
|
||||
{
|
||||
// Add parameter to Command.Parameters
|
||||
string paramName = string.Concat(_paramPrefix, "P", _command.Parameters.Count.ToString());
|
||||
|
||||
_sb.Append(paramName);
|
||||
|
||||
var parameter = new ParameterChainMethods(_command, paramName, expression.Value).Parameter;
|
||||
return expression;
|
||||
}
|
||||
|
||||
private object GetRightValue(Expression rightExpression)
|
||||
{
|
||||
object rightValue = null;
|
||||
|
||||
var right = rightExpression as ConstantExpression;
|
||||
if (right == null) // Value is not directly passed in as a constant
|
||||
{
|
||||
var rightMemberExp = (rightExpression as MemberExpression);
|
||||
var parentMemberExpression = rightMemberExp.Expression as MemberExpression;
|
||||
if (parentMemberExpression != null) // Value is passed in as a property on a parent entity
|
||||
{
|
||||
string entityName = (rightMemberExp.Expression as MemberExpression).Member.Name;
|
||||
var container = ((rightMemberExp.Expression as MemberExpression).Expression as ConstantExpression).Value;
|
||||
var entity = _repos.ReflectionStrategy.GetFieldValue(container, entityName);
|
||||
rightValue = _repos.ReflectionStrategy.GetFieldValue(entity, rightMemberExp.Member.Name);
|
||||
}
|
||||
else // Value is passed in as a variable
|
||||
{
|
||||
var parent = (rightMemberExp.Expression as ConstantExpression).Value;
|
||||
rightValue = _repos.ReflectionStrategy.GetFieldValue(parent, rightMemberExp.Member.Name);
|
||||
}
|
||||
}
|
||||
else // Value is passed in directly as a constant
|
||||
{
|
||||
rightValue = right.Value;
|
||||
}
|
||||
|
||||
return rightValue;
|
||||
}
|
||||
|
||||
protected string GetFullyQualifiedColumnName(MemberInfo member, Type declaringType)
|
||||
{
|
||||
if (_tablePrefix)
|
||||
{
|
||||
Table table = _tables.FindTable(declaringType);
|
||||
|
||||
if (table == null)
|
||||
{
|
||||
string msg = string.Format("The property '{0} -> {1}' you are trying to reference in the 'WHERE' 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.",
|
||||
declaringType,
|
||||
member.Name);
|
||||
|
||||
throw new DataMappingException(msg);
|
||||
}
|
||||
|
||||
string columnName = DataHelper.GetColumnName(declaringType, member.Name, _useAltName);
|
||||
return _dialect.CreateToken(string.Format("{0}.{1}", table.Alias, columnName));
|
||||
}
|
||||
else
|
||||
{
|
||||
string columnName = DataHelper.GetColumnName(declaringType, member.Name, _useAltName);
|
||||
return _dialect.CreateToken(columnName);
|
||||
}
|
||||
}
|
||||
|
||||
private string Decode(ExpressionType expType)
|
||||
{
|
||||
switch (expType)
|
||||
{
|
||||
case ExpressionType.AndAlso: return "AND";
|
||||
case ExpressionType.And: return "AND";
|
||||
case ExpressionType.Equal: return "=";
|
||||
case ExpressionType.GreaterThan: return ">";
|
||||
case ExpressionType.GreaterThanOrEqual: return ">=";
|
||||
case ExpressionType.LessThan: return "<";
|
||||
case ExpressionType.LessThanOrEqual: return "<=";
|
||||
case ExpressionType.NotEqual: return "<>";
|
||||
case ExpressionType.OrElse: return "OR";
|
||||
case ExpressionType.Or: return "OR";
|
||||
default: throw new NotSupportedException(string.Format("{0} statement is not supported", expType.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
private void Write_Contains(MethodCallExpression body)
|
||||
{
|
||||
// Add parameter to Command.Parameters
|
||||
object value = GetRightValue(body.Arguments[0]);
|
||||
string paramName = string.Concat(_paramPrefix, "P", _command.Parameters.Count.ToString());
|
||||
var parameter = new ParameterChainMethods(_command, paramName, value).Parameter;
|
||||
|
||||
MemberExpression memberExp = (body.Object as MemberExpression);
|
||||
string fqColumn = GetFullyQualifiedColumnName(memberExp.Member, memberExp.Expression.Type);
|
||||
_sb.AppendFormat("({0} LIKE '%' + {1} + '%')", fqColumn, paramName);
|
||||
}
|
||||
|
||||
private void Write_StartsWith(MethodCallExpression body)
|
||||
{
|
||||
// Add parameter to Command.Parameters
|
||||
object value = GetRightValue(body.Arguments[0]);
|
||||
string paramName = string.Concat(_paramPrefix, "P", _command.Parameters.Count.ToString());
|
||||
var parameter = new ParameterChainMethods(_command, paramName, value).Parameter;
|
||||
|
||||
MemberExpression memberExp = (body.Object as MemberExpression);
|
||||
string fqColumn = GetFullyQualifiedColumnName(memberExp.Member, memberExp.Expression.Type);
|
||||
_sb.AppendFormat("({0} LIKE {1} + '%')", fqColumn, paramName);
|
||||
}
|
||||
|
||||
private void Write_EndsWith(MethodCallExpression body)
|
||||
{
|
||||
// Add parameter to Command.Parameters
|
||||
object value = GetRightValue(body.Arguments[0]);
|
||||
string paramName = string.Concat(_paramPrefix, "P", _command.Parameters.Count.ToString());
|
||||
var parameter = new ParameterChainMethods(_command, paramName, value).Parameter;
|
||||
|
||||
MemberExpression memberExp = (body.Object as MemberExpression);
|
||||
string fqColumn = GetFullyQualifiedColumnName(memberExp.Member, memberExp.Expression.Type);
|
||||
_sb.AppendFormat("({0} LIKE '%' + {1})", fqColumn, paramName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the current where clause with another where clause.
|
||||
/// </summary>
|
||||
/// <param name="where">The second where clause that is being appended.</param>
|
||||
/// <param name="appendType">AND / OR</param>
|
||||
internal void Append(WhereBuilder<T> where, WhereAppendType appendType)
|
||||
{
|
||||
_constantWhereClause = string.Format("{0} {1} {2}",
|
||||
this.ToString(),
|
||||
appendType.ToString(),
|
||||
where.ToString().Replace("WHERE ", string.Empty));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_constantWhereClause))
|
||||
{
|
||||
return _sb.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _constantWhereClause;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum WhereAppendType
|
||||
{
|
||||
AND,
|
||||
OR
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user