Added missing files
This commit is contained in:
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Some of methods in this code file is based on Kerosene ORM solution
|
||||||
|
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using DynamORM.Builders.Implementation;
|
||||||
|
using DynamORM.Helpers;
|
||||||
|
using DynamORM.Mapper;
|
||||||
|
|
||||||
|
namespace DynamORM.Builders.Extensions
|
||||||
|
{
|
||||||
|
internal static class DynamicModifyBuilderExtensions
|
||||||
|
{
|
||||||
|
internal static T Table<T>(this T builder, string tableName, Dictionary<string, DynamicSchemaColumn> schema = null) where T : DynamicModifyBuilder
|
||||||
|
{
|
||||||
|
var tuple = tableName.Validated("Table Name").SplitSomethingAndAlias();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(tuple.Item2))
|
||||||
|
throw new ArgumentException(string.Format("Can not use aliases in INSERT steatement. ({0})", tableName), "tableName");
|
||||||
|
|
||||||
|
var parts = tuple.Item1.Split('.');
|
||||||
|
|
||||||
|
builder.Tables.Clear();
|
||||||
|
builder.Tables.Add(new DynamicQueryBuilder.TableInfo(builder.Database,
|
||||||
|
builder.Database.StripName(parts.Last()).Validated("Table"), null,
|
||||||
|
parts.Length == 2 ? builder.Database.StripName(parts.First()).Validated("Owner", canbeNull: true) : null));
|
||||||
|
|
||||||
|
if (schema != null)
|
||||||
|
(builder.Tables[0] as DynamicQueryBuilder.TableInfo).Schema = schema;
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T Table<T>(this T builder, Type type) where T : DynamicQueryBuilder
|
||||||
|
{
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(type);
|
||||||
|
string name = string.Empty;
|
||||||
|
|
||||||
|
if (mapper == null)
|
||||||
|
throw new InvalidOperationException("Cant assign unmapable type as a table.");
|
||||||
|
|
||||||
|
if (builder is DynamicModifyBuilder)
|
||||||
|
{
|
||||||
|
builder.Tables.Clear();
|
||||||
|
builder.Tables.Add(new DynamicQueryBuilder.TableInfo(builder.Database, type));
|
||||||
|
}
|
||||||
|
else if (builder is DynamicSelectQueryBuilder)
|
||||||
|
(builder as DynamicSelectQueryBuilder).From(x => x(type));
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
232
DynamORM/Builders/Extensions/DynamicWhereQueryExtensions.cs
Normal file
232
DynamORM/Builders/Extensions/DynamicWhereQueryExtensions.cs
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Some of methods in this code file is based on Kerosene ORM solution
|
||||||
|
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using DynamORM.Builders.Implementation;
|
||||||
|
using DynamORM.Helpers;
|
||||||
|
using DynamORM.Helpers.Dynamics;
|
||||||
|
using DynamORM.Mapper;
|
||||||
|
|
||||||
|
namespace DynamORM.Builders.Extensions
|
||||||
|
{
|
||||||
|
internal static class DynamicWhereQueryExtensions
|
||||||
|
{
|
||||||
|
#region Where
|
||||||
|
|
||||||
|
internal static T InternalWhere<T>(this T builder, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
|
{
|
||||||
|
if (func == null) throw new ArgumentNullException("Array of functions cannot be null.");
|
||||||
|
|
||||||
|
using (var parser = DynamicParser.Parse(func))
|
||||||
|
{
|
||||||
|
string condition = null;
|
||||||
|
bool and = true;
|
||||||
|
|
||||||
|
var result = parser.Result;
|
||||||
|
if (result is string)
|
||||||
|
{
|
||||||
|
condition = (string)result;
|
||||||
|
|
||||||
|
if (condition.ToUpper().IndexOf("OR") == 0)
|
||||||
|
{
|
||||||
|
and = false;
|
||||||
|
condition = condition.Substring(3);
|
||||||
|
}
|
||||||
|
else if (condition.ToUpper().IndexOf("AND") == 0)
|
||||||
|
condition = condition.Substring(4);
|
||||||
|
}
|
||||||
|
else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType)
|
||||||
|
return builder.InternalWhere(result);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Intercepting the 'x => x.And()' and 'x => x.Or()' virtual methods...
|
||||||
|
if (result is DynamicParser.Node.Method && ((DynamicParser.Node.Method)result).Host is DynamicParser.Node.Argument)
|
||||||
|
{
|
||||||
|
var node = (DynamicParser.Node.Method)result;
|
||||||
|
var name = node.Name.ToUpper();
|
||||||
|
if (name == "AND" || name == "OR")
|
||||||
|
{
|
||||||
|
object[] args = ((DynamicParser.Node.Method)node).Arguments;
|
||||||
|
if (args == null) throw new ArgumentNullException("arg", string.Format("{0} is not a parameterless method.", name));
|
||||||
|
if (args.Length != 1) throw new ArgumentException(string.Format("{0} requires one and only one parameter: {1}.", name, args.Sketch()));
|
||||||
|
|
||||||
|
and = name == "AND" ? true : false;
|
||||||
|
result = args[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just parsing the contents now...
|
||||||
|
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builder.WhereCondition == null) builder.WhereCondition = condition;
|
||||||
|
else builder.WhereCondition = string.Format("{0} {1} {2}", builder.WhereCondition, and ? "AND" : "OR", condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T InternalWhere<T>(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
|
{
|
||||||
|
builder.VirtualMode = column.VirtualColumn;
|
||||||
|
bool prepend = false;
|
||||||
|
|
||||||
|
if (column.BeginBlock)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(builder.WhereCondition))
|
||||||
|
prepend = true;
|
||||||
|
else
|
||||||
|
builder.WhereCondition += " (";
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's kind of uglu, but... well it works.
|
||||||
|
if (column.Or)
|
||||||
|
switch (column.Operator)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) == column.Value)); break;
|
||||||
|
case DynamicColumn.CompareOperator.Not: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) != column.Value)); break;
|
||||||
|
case DynamicColumn.CompareOperator.Like: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(column.Value))); break;
|
||||||
|
case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value))); break;
|
||||||
|
case DynamicColumn.CompareOperator.In: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(column.Value))); break;
|
||||||
|
case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) < column.Value)); break;
|
||||||
|
case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) <= column.Value)); break;
|
||||||
|
case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) > column.Value)); break;
|
||||||
|
case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= column.Value)); break;
|
||||||
|
case DynamicColumn.CompareOperator.Between: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)).Between(column.Value))); break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
switch (column.Operator)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) == column.Value); break;
|
||||||
|
case DynamicColumn.CompareOperator.Not: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) != column.Value); break;
|
||||||
|
case DynamicColumn.CompareOperator.Like: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)).Like(column.Value)); break;
|
||||||
|
case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value)); break;
|
||||||
|
case DynamicColumn.CompareOperator.In: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)).In(column.Value)); break;
|
||||||
|
case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) < column.Value); break;
|
||||||
|
case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) <= column.Value); break;
|
||||||
|
case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) > column.Value); break;
|
||||||
|
case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) >= column.Value); break;
|
||||||
|
case DynamicColumn.CompareOperator.Between: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)).Between(column.Value)); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prepend)
|
||||||
|
builder.WhereCondition = string.Format("({0}", builder.WhereCondition);
|
||||||
|
|
||||||
|
if (column.EndBlock)
|
||||||
|
builder.WhereCondition += ")";
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T InternalWhere<T>(this T builder, string column, DynamicColumn.CompareOperator op, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
|
{
|
||||||
|
if (value is DynamicColumn)
|
||||||
|
{
|
||||||
|
var v = (DynamicColumn)value;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(v.ColumnName))
|
||||||
|
v.ColumnName = column;
|
||||||
|
|
||||||
|
return builder.InternalWhere(v);
|
||||||
|
}
|
||||||
|
else if (value is IEnumerable<DynamicColumn>)
|
||||||
|
{
|
||||||
|
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)value)
|
||||||
|
builder.InternalWhere(v);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.InternalWhere(new DynamicColumn
|
||||||
|
{
|
||||||
|
ColumnName = column,
|
||||||
|
Operator = op,
|
||||||
|
Value = value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T InternalWhere<T>(this T builder, string column, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
|
{
|
||||||
|
return builder.InternalWhere(column, DynamicColumn.CompareOperator.Eq, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T InternalWhere<T>(this T builder, object conditions, bool schema = false) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
|
{
|
||||||
|
if (conditions is DynamicColumn)
|
||||||
|
return builder.InternalWhere((DynamicColumn)conditions);
|
||||||
|
else if (conditions is IEnumerable<DynamicColumn>)
|
||||||
|
{
|
||||||
|
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)conditions)
|
||||||
|
builder.InternalWhere(v);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dict = conditions.ToDictionary();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(conditions.GetType());
|
||||||
|
var table = dict.TryGetValue("_table").NullOr(x => x.ToString(), string.Empty);
|
||||||
|
|
||||||
|
foreach (var condition in dict)
|
||||||
|
{
|
||||||
|
if (mapper.Ignored.Contains(condition.Key) || condition.Key == "_table")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string colName = mapper != null ? mapper.PropertyMap.TryGetValue(condition.Key) ?? condition.Key : condition.Key;
|
||||||
|
|
||||||
|
DynamicSchemaColumn? col = null;
|
||||||
|
|
||||||
|
// This should be used on typed queries or update/delete steatements, which usualy operate on a single table.
|
||||||
|
if (schema)
|
||||||
|
{
|
||||||
|
col = builder.GetColumnFromSchema(colName, mapper, table);
|
||||||
|
|
||||||
|
if ((!col.HasValue || !col.Value.IsKey) &&
|
||||||
|
(mapper == null || mapper.ColumnsMap.TryGetValue(colName).NullOr(m => m.Ignore || m.Column.NullOr(c => !c.IsKey, true), true)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
colName = col.HasValue ? col.Value.Name : colName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(table))
|
||||||
|
builder.InternalWhere(x => x(builder.FixObjectName(string.Format("{0}.{1}", table, colName))) == condition.Value);
|
||||||
|
else
|
||||||
|
builder.InternalWhere(x => x(builder.FixObjectName(colName)) == condition.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Where
|
||||||
|
}
|
||||||
|
}
|
||||||
127
DynamORM/Builders/Implementation/DynamicDeleteQueryBuilder.cs
Normal file
127
DynamORM/Builders/Implementation/DynamicDeleteQueryBuilder.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Some of methods in this code file is based on Kerosene ORM solution
|
||||||
|
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using DynamORM.Builders.Extensions;
|
||||||
|
|
||||||
|
namespace DynamORM.Builders.Implementation
|
||||||
|
{
|
||||||
|
/// <summary>Implementation of dynamic delete query builder.</summary>
|
||||||
|
internal class DynamicDeleteQueryBuilder : DynamicModifyBuilder, IDynamicDeleteQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicDeleteQueryBuilder"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
internal DynamicDeleteQueryBuilder(DynamicDatabase db)
|
||||||
|
: base(db)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicDeleteQueryBuilder"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <param name="tableName">Name of the table.</param>
|
||||||
|
public DynamicDeleteQueryBuilder(DynamicDatabase db, string tableName)
|
||||||
|
: base(db, tableName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Generates the text this command will execute against the underlying database.</summary>
|
||||||
|
/// <returns>The text to execute against the underlying database.</returns>
|
||||||
|
/// <remarks>This method must be override by derived classes.</remarks>
|
||||||
|
public override string CommandText()
|
||||||
|
{
|
||||||
|
var info = Tables.Single();
|
||||||
|
return string.Format("DELETE FROM {0}{1} WHERE {2}",
|
||||||
|
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
||||||
|
Database.DecorateName(info.Name), WhereCondition);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Where
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||||
|
/// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used
|
||||||
|
/// as needed.
|
||||||
|
/// <para>- If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||||
|
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||||
|
/// 'Where( x => x.Or( condition ) )'.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The specification.</param>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicDeleteQueryBuilder Where(Func<dynamic, object> func)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add where condition.</summary>
|
||||||
|
/// <param name="column">Condition column with operator and value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicDeleteQueryBuilder Where(DynamicColumn column)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add where condition.</summary>
|
||||||
|
/// <param name="column">Condition column.</param>
|
||||||
|
/// <param name="op">Condition operator.</param>
|
||||||
|
/// <param name="value">Condition value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicDeleteQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(column, op, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add where condition.</summary>
|
||||||
|
/// <param name="column">Condition column.</param>
|
||||||
|
/// <param name="value">Condition value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicDeleteQueryBuilder Where(string column, object value)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(column, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add where condition.</summary>
|
||||||
|
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||||
|
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||||
|
/// aren't keys.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicDeleteQueryBuilder Where(object conditions, bool schema = false)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(conditions, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Where
|
||||||
|
}
|
||||||
|
}
|
||||||
201
DynamORM/Builders/Implementation/DynamicInsertQueryBuilder.cs
Normal file
201
DynamORM/Builders/Implementation/DynamicInsertQueryBuilder.cs
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Some of methods in this code file is based on Kerosene ORM solution
|
||||||
|
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using DynamORM.Helpers.Dynamics;
|
||||||
|
using DynamORM.Mapper;
|
||||||
|
|
||||||
|
namespace DynamORM.Builders.Implementation
|
||||||
|
{
|
||||||
|
/// <summary>Implementation of dynamic insert query builder.</summary>
|
||||||
|
internal class DynamicInsertQueryBuilder : DynamicModifyBuilder, IDynamicInsertQueryBuilder
|
||||||
|
{
|
||||||
|
private string _columns;
|
||||||
|
private string _values;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicInsertQueryBuilder"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
internal DynamicInsertQueryBuilder(DynamicDatabase db)
|
||||||
|
: base(db)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicInsertQueryBuilder"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <param name="tableName">Name of the table.</param>
|
||||||
|
public DynamicInsertQueryBuilder(DynamicDatabase db, string tableName)
|
||||||
|
: base(db, tableName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Generates the text this command will execute against the underlying database.</summary>
|
||||||
|
/// <returns>The text to execute against the underlying database.</returns>
|
||||||
|
/// <remarks>This method must be override by derived classes.</remarks>
|
||||||
|
public override string CommandText()
|
||||||
|
{
|
||||||
|
var info = Tables.Single();
|
||||||
|
return string.Format("INSERT INTO {0}{1} ({2}) VALUES ({3})",
|
||||||
|
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
||||||
|
Database.DecorateName(info.Name), _columns, _values);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Insert
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the columns to insert using the dynamic lambda expressions given. Each expression correspond to one
|
||||||
|
/// column, and can:
|
||||||
|
/// <para>- Resolve to a string, in this case a '=' must appear in the string.</para>
|
||||||
|
/// <para>- Resolve to a expression with the form: 'x => x.Column = Value'.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The specifications.</param>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicInsertQueryBuilder Insert(params Func<dynamic, object>[] func)
|
||||||
|
{
|
||||||
|
if (func == null)
|
||||||
|
throw new ArgumentNullException("Array of specifications cannot be null.");
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
|
foreach (var f in func)
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
|
||||||
|
if (f == null)
|
||||||
|
throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index));
|
||||||
|
|
||||||
|
using (var parser = DynamicParser.Parse(f))
|
||||||
|
{
|
||||||
|
var result = parser.Result;
|
||||||
|
if (result == null)
|
||||||
|
throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index));
|
||||||
|
|
||||||
|
string main = null;
|
||||||
|
string value = null;
|
||||||
|
string str = null;
|
||||||
|
|
||||||
|
// When 'x => x.Table.Column = value' or 'x => x.Column = value'...
|
||||||
|
if (result is DynamicParser.Node.SetMember)
|
||||||
|
{
|
||||||
|
var node = (DynamicParser.Node.SetMember)result;
|
||||||
|
|
||||||
|
DynamicSchemaColumn? col = GetColumnFromSchema(node.Name);
|
||||||
|
main = Database.DecorateName(node.Name);
|
||||||
|
value = Parse(node.Value, pars: Parameters, nulls: true, columnSchema: col);
|
||||||
|
|
||||||
|
_columns = _columns == null ? main : string.Format("{0}, {1}", _columns, main);
|
||||||
|
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType)
|
||||||
|
{
|
||||||
|
Insert(result);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other specifications are considered invalid...
|
||||||
|
var err = string.Format("Specification '{0}' is invalid.", result);
|
||||||
|
str = Parse(result);
|
||||||
|
if (str.Contains("=")) err += " May have you used a '==' instead of a '=' operator?";
|
||||||
|
throw new ArgumentException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add insert fields.</summary>
|
||||||
|
/// <param name="column">Insert column and value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicInsertQueryBuilder Insert(DynamicColumn column)
|
||||||
|
{
|
||||||
|
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
|
||||||
|
|
||||||
|
string main = FixObjectName(column.ColumnName, onlyColumn: true);
|
||||||
|
string value = Parse(column.Value, pars: Parameters, nulls: true, columnSchema: col);
|
||||||
|
|
||||||
|
_columns = _columns == null ? main : string.Format("{0}, {1}", _columns, main);
|
||||||
|
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add insert fields.</summary>
|
||||||
|
/// <param name="column">Insert column.</param>
|
||||||
|
/// <param name="value">Insert value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicInsertQueryBuilder Insert(string column, object value)
|
||||||
|
{
|
||||||
|
if (value is DynamicColumn)
|
||||||
|
{
|
||||||
|
var v = (DynamicColumn)value;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(v.ColumnName))
|
||||||
|
v.ColumnName = column;
|
||||||
|
|
||||||
|
return Insert(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Insert(new DynamicColumn
|
||||||
|
{
|
||||||
|
ColumnName = column,
|
||||||
|
Value = value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add insert fields.</summary>
|
||||||
|
/// <param name="o">Set insert value as properties and values of an object.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicInsertQueryBuilder Insert(object o)
|
||||||
|
{
|
||||||
|
var dict = o.ToDictionary();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(o.GetType());
|
||||||
|
|
||||||
|
if (mapper != null)
|
||||||
|
{
|
||||||
|
foreach (var con in dict)
|
||||||
|
if (!mapper.Ignored.Contains(con.Key))
|
||||||
|
Insert(mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key, con.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
foreach (var con in dict)
|
||||||
|
Insert(con.Key, con.Value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Insert
|
||||||
|
}
|
||||||
|
}
|
||||||
71
DynamORM/Builders/Implementation/DynamicModifyBuilder.cs
Normal file
71
DynamORM/Builders/Implementation/DynamicModifyBuilder.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using DynamORM.Builders.Extensions;
|
||||||
|
|
||||||
|
namespace DynamORM.Builders.Implementation
|
||||||
|
{
|
||||||
|
/// <summary>Base query builder for insert/update/delete statements.</summary>
|
||||||
|
internal abstract class DynamicModifyBuilder : DynamicQueryBuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicModifyBuilder"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
public DynamicModifyBuilder(DynamicDatabase db)
|
||||||
|
: base(db)
|
||||||
|
{
|
||||||
|
VirtualMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicModifyBuilder" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <param name="tableName">Name of the table.</param>
|
||||||
|
public DynamicModifyBuilder(DynamicDatabase db, string tableName)
|
||||||
|
: this(db)
|
||||||
|
{
|
||||||
|
VirtualMode = false;
|
||||||
|
this.Table(tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Execute this builder.</summary>
|
||||||
|
/// <returns>Result of an execution..</returns>
|
||||||
|
public virtual int Execute()
|
||||||
|
{
|
||||||
|
using (var con = Database.Open())
|
||||||
|
using (var cmd = con.CreateCommand())
|
||||||
|
{
|
||||||
|
return cmd
|
||||||
|
.SetCommand(this)
|
||||||
|
.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
744
DynamORM/Builders/Implementation/DynamicQueryBuilder.cs
Normal file
744
DynamORM/Builders/Implementation/DynamicQueryBuilder.cs
Normal file
@@ -0,0 +1,744 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Some of methods in this code file is based on Kerosene ORM solution
|
||||||
|
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text;
|
||||||
|
using DynamORM.Helpers;
|
||||||
|
using DynamORM.Helpers.Dynamics;
|
||||||
|
using DynamORM.Mapper;
|
||||||
|
|
||||||
|
namespace DynamORM.Builders.Implementation
|
||||||
|
{
|
||||||
|
/// <summary>Implementation of dynamic query builder base interface.</summary>
|
||||||
|
internal abstract class DynamicQueryBuilder : IDynamicQueryBuilder
|
||||||
|
{
|
||||||
|
/// <summary>Empty interface to allow where query builder implementation use universal approach.</summary>
|
||||||
|
internal interface IQueryWithWhere { }
|
||||||
|
|
||||||
|
private DynamicQueryBuilder _parent = null;
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether add virtual.</summary>
|
||||||
|
internal bool VirtualMode { get; set; }
|
||||||
|
|
||||||
|
#region TableInfo
|
||||||
|
|
||||||
|
/// <summary>Table information.</summary>
|
||||||
|
internal class TableInfo : ITableInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TableInfo"/> class.
|
||||||
|
/// </summary>
|
||||||
|
internal TableInfo()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TableInfo" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <param name="name">The name of table.</param>
|
||||||
|
/// <param name="alias">The table alias.</param>
|
||||||
|
/// <param name="owner">The table owner.</param>
|
||||||
|
public TableInfo(DynamicDatabase db, string name, string alias = null, string owner = null)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Alias = alias;
|
||||||
|
Owner = owner;
|
||||||
|
|
||||||
|
if (!name.ContainsAny(StringExtensions.InvalidMemberChars))
|
||||||
|
Schema = db.GetSchema(name, owner: owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TableInfo" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <param name="type">The type which can be mapped to database.</param>
|
||||||
|
/// <param name="alias">The table alias.</param>
|
||||||
|
/// <param name="owner">The table owner.</param>
|
||||||
|
public TableInfo(DynamicDatabase db, Type type, string alias = null, string owner = null)
|
||||||
|
{
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(type);
|
||||||
|
|
||||||
|
Name = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
|
||||||
|
mapper.Type.Name : mapper.Table.Name;
|
||||||
|
|
||||||
|
Owner = (mapper.Table != null) ? mapper.Table.Owner : owner;
|
||||||
|
Alias = alias;
|
||||||
|
|
||||||
|
Schema = db.GetSchema(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets or sets table owner name.</summary>
|
||||||
|
public string Owner { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets table name.</summary>
|
||||||
|
public string Name { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets table alias.</summary>
|
||||||
|
public string Alias { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets table schema.</summary>
|
||||||
|
public Dictionary<string, DynamicSchemaColumn> Schema { get; internal set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Generic based table information.</summary>
|
||||||
|
/// <typeparam name="T">Type of class that is represented in database.</typeparam>
|
||||||
|
internal class TableInfo<T> : TableInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TableInfo{T}" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <param name="alias">The table alias.</param>
|
||||||
|
/// <param name="owner">The table owner.</param>
|
||||||
|
public TableInfo(DynamicDatabase db, string alias = null, string owner = null)
|
||||||
|
: base(db, typeof(T), alias, owner)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion TableInfo
|
||||||
|
|
||||||
|
#region Parameter
|
||||||
|
|
||||||
|
/// <summary>Interface describing parameter info.</summary>
|
||||||
|
internal class Parameter : IParameter
|
||||||
|
{
|
||||||
|
/// <summary>Gets or sets the parameter temporary name.</summary>
|
||||||
|
public string Name { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the parameter value.</summary>
|
||||||
|
public object Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether this <see cref="Parameter"/> is virtual.</summary>
|
||||||
|
public bool Virtual { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the parameter schema information.</summary>
|
||||||
|
public DynamicSchemaColumn? Schema { get; internal set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Parameter
|
||||||
|
|
||||||
|
internal string WhereCondition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets <see cref="DynamicDatabase"/> instance.</summary>
|
||||||
|
public DynamicDatabase Database { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the tables used in this builder.</summary>
|
||||||
|
public IList<ITableInfo> Tables { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the tables used in this builder.</summary>
|
||||||
|
public IDictionary<string, IParameter> Parameters { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether database supports standard schema.</summary>
|
||||||
|
public bool SupportSchema { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicQueryBuilder"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
public DynamicQueryBuilder(DynamicDatabase db)
|
||||||
|
{
|
||||||
|
VirtualMode = false;
|
||||||
|
Tables = new List<ITableInfo>();
|
||||||
|
Parameters = new Dictionary<string, IParameter>();
|
||||||
|
|
||||||
|
Database = db;
|
||||||
|
SupportSchema = (db.Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="DynamicQueryBuilder"/> class.</summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <param name="parent">The parent query.</param>
|
||||||
|
internal DynamicQueryBuilder(DynamicDatabase db, DynamicQueryBuilder parent)
|
||||||
|
: this(db)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool IsTableAlias(string name)
|
||||||
|
{
|
||||||
|
DynamicQueryBuilder builder = this;
|
||||||
|
|
||||||
|
while (builder != null)
|
||||||
|
{
|
||||||
|
if (builder.Tables.Any(t => t.Alias == name))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
builder = builder._parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool IsTable(string name, string owner)
|
||||||
|
{
|
||||||
|
DynamicQueryBuilder builder = this;
|
||||||
|
|
||||||
|
while (builder != null)
|
||||||
|
{
|
||||||
|
if ((string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower())) ||
|
||||||
|
(!string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower() &&
|
||||||
|
!string.IsNullOrEmpty(t.Owner) && t.Owner.ToLower() == owner.ToLower())))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
builder = builder._parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates sub query.</summary>
|
||||||
|
/// <returns>Sub query builder.</returns>
|
||||||
|
public IDynamicSelectQueryBuilder SubQuery()
|
||||||
|
{
|
||||||
|
return new DynamicSelectQueryBuilder(Database, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds to the 'From' clause of sub query the contents obtained by
|
||||||
|
/// parsing the dynamic lambda expressions given. The supported formats are:
|
||||||
|
/// <para>- Resolve to a string: 'x => "Table AS Alias', where the alias part is optional.</para>
|
||||||
|
/// <para>- Resolve to an expression: 'x => x.Table.As( x.Alias )', where the alias part is optional.</para>
|
||||||
|
/// <para>- Generic expression: 'x => x( expression ).As( x.Alias )', where the alias part is mandatory. In this
|
||||||
|
/// case the alias is not annotated.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The specification.</param>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public IDynamicSelectQueryBuilder SubQuery(params Func<dynamic, object>[] func)
|
||||||
|
{
|
||||||
|
return SubQuery().From(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates the text this command will execute against the underlying database.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The text to execute against the underlying database.</returns>
|
||||||
|
/// <remarks>This method must be override by derived classes.</remarks>
|
||||||
|
public abstract string CommandText();
|
||||||
|
|
||||||
|
/// <summary>Fill command with query.</summary>
|
||||||
|
/// <param name="command">Command to fill.</param>
|
||||||
|
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
||||||
|
public virtual IDbCommand FillCommand(IDbCommand command)
|
||||||
|
{
|
||||||
|
return command.SetCommand(CommandText()
|
||||||
|
.FillStringWithVariables(s =>
|
||||||
|
{
|
||||||
|
return Parameters.TryGetValue(s).NullOr(p =>
|
||||||
|
{
|
||||||
|
return ((IDbDataParameter)command
|
||||||
|
.AddParameter(this, p.Schema, p.Value)
|
||||||
|
.Parameters[command.Parameters.Count - 1])
|
||||||
|
.ParameterName;
|
||||||
|
}, s);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal DynamicSchemaColumn? GetColumnFromSchema(string colName, DynamicTypeMap mapper = null, string table = null)
|
||||||
|
{
|
||||||
|
// This is tricky and will not always work unfortunetly.
|
||||||
|
if (colName.ContainsAny(StringExtensions.InvalidMultipartMemberChars))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// First we need to get real column name and it's owner if exist.
|
||||||
|
var parts = colName.Split('.')
|
||||||
|
.Select(c => Database.StripName(c))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var columnName = parts.Last();
|
||||||
|
|
||||||
|
// Get table name from mapper
|
||||||
|
string tableName = table;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(tableName))
|
||||||
|
{
|
||||||
|
tableName = (mapper != null && mapper.Table != null) ? mapper.Table.Name : string.Empty;
|
||||||
|
|
||||||
|
if (parts.Length > 1 && string.IsNullOrEmpty(tableName))
|
||||||
|
{
|
||||||
|
// OK, we have a multi part identifier, that's good, we can get table name
|
||||||
|
tableName = string.Join(".", parts.Take(parts.Length - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get table info from cache
|
||||||
|
var tableInfo = !string.IsNullOrEmpty(tableName) ?
|
||||||
|
Tables.FirstOrDefault(x => !string.IsNullOrEmpty(x.Alias) && x.Alias.ToLower() == tableName) ??
|
||||||
|
Tables.FirstOrDefault(x => x.Name.ToLower() == tableName.ToLower()) ?? Tables.FirstOrDefault() :
|
||||||
|
this is DynamicModifyBuilder ? Tables.FirstOrDefault() : null;
|
||||||
|
|
||||||
|
// Try to get column from schema
|
||||||
|
if (tableInfo != null && tableInfo.Schema != null)
|
||||||
|
return tableInfo.Schema.TryGetNullable(columnName.ToLower());
|
||||||
|
|
||||||
|
// Well, we failed to find a column
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Parser
|
||||||
|
|
||||||
|
/// <summary>Parses the arbitrary object given and translates it into a string with the appropriate
|
||||||
|
/// syntax for the database this parser is specific to.</summary>
|
||||||
|
/// <param name="node">The object to parse and translate. It can be any arbitrary object, including null values (if
|
||||||
|
/// permitted) and dynamic lambda expressions.</param>
|
||||||
|
/// <param name="pars">If not null, the parameters' list where to store the parameters extracted by the parsing.</param>
|
||||||
|
/// <param name="rawstr">If true, literal (raw) string are allowed. If false and the node is a literal then, as a
|
||||||
|
/// security measure, an exception is thrown.</param>
|
||||||
|
/// <param name="nulls">True to accept null values and translate them into the appropriate syntax accepted by the
|
||||||
|
/// database. If false and the value is null, then an exception is thrown.</param>
|
||||||
|
/// <param name="decorate">If set to <c>true</c> decorate element.</param>
|
||||||
|
/// <param name="isMultiPart">If set parse argument as alias. This is workaround for AS method.</param>
|
||||||
|
/// <param name="columnSchema">This parameter is used to determine type of parameter used in query.</param>
|
||||||
|
/// <returns>A string containing the result of the parsing, along with the parameters extracted in the
|
||||||
|
/// <see cref="pars" /> instance if such is given.</returns>
|
||||||
|
/// <exception cref="System.ArgumentNullException">Null nodes are not accepted.</exception>
|
||||||
|
internal virtual string Parse(object node, IDictionary<string, IParameter> pars = null, bool rawstr = false, bool nulls = false, bool decorate = true, bool isMultiPart = true, DynamicSchemaColumn? columnSchema = null)
|
||||||
|
{
|
||||||
|
// Null nodes are accepted or not depending upon the "nulls" flag...
|
||||||
|
if (node == null)
|
||||||
|
{
|
||||||
|
if (!nulls)
|
||||||
|
throw new ArgumentNullException("node", "Null nodes are not accepted.");
|
||||||
|
|
||||||
|
return Dispatch(node, pars, decorate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nodes that are strings are parametrized or not depending the "rawstr" flag...
|
||||||
|
if (node is string)
|
||||||
|
{
|
||||||
|
if (rawstr) return (string)node;
|
||||||
|
else return Dispatch(node, pars, decorate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If node is a delegate, parse it to create the logical tree...
|
||||||
|
if (node is Delegate)
|
||||||
|
{
|
||||||
|
node = DynamicParser.Parse((Delegate)node).Result;
|
||||||
|
return Parse(node, pars, rawstr, decorate: decorate, columnSchema: columnSchema); // Intercept containers as in (x => "string")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dispatch(node, pars, decorate, isMultiPart, columnSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string Dispatch(object node, IDictionary<string, IParameter> pars = null, bool decorate = true, bool isMultiPart = true, DynamicSchemaColumn? columnSchema = null)
|
||||||
|
{
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
if (node is DynamicQueryBuilder) return ParseCommand((DynamicQueryBuilder)node, pars);
|
||||||
|
else if (node is DynamicParser.Node.Argument) return ParseArgument((DynamicParser.Node.Argument)node, isMultiPart);
|
||||||
|
else if (node is DynamicParser.Node.GetMember) return ParseGetMember((DynamicParser.Node.GetMember)node, pars, decorate, isMultiPart, columnSchema);
|
||||||
|
else if (node is DynamicParser.Node.SetMember) return ParseSetMember((DynamicParser.Node.SetMember)node, pars, decorate, isMultiPart, columnSchema);
|
||||||
|
else if (node is DynamicParser.Node.Unary) return ParseUnary((DynamicParser.Node.Unary)node, pars);
|
||||||
|
else if (node is DynamicParser.Node.Binary) return ParseBinary((DynamicParser.Node.Binary)node, pars);
|
||||||
|
else if (node is DynamicParser.Node.Method) return ParseMethod((DynamicParser.Node.Method)node, pars);
|
||||||
|
else if (node is DynamicParser.Node.Invoke) return ParseInvoke((DynamicParser.Node.Invoke)node, pars);
|
||||||
|
else if (node is DynamicParser.Node.Convert) return ParseConvert((DynamicParser.Node.Convert)node, pars);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other cases are considered constant parameters...
|
||||||
|
return ParseConstant(node, pars, columnSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseCommand(DynamicQueryBuilder node, IDictionary<string, IParameter> pars = null)
|
||||||
|
{
|
||||||
|
// Getting the command's text...
|
||||||
|
string str = node.CommandText(); // Avoiding spurious "OUTPUT XXX" statements
|
||||||
|
|
||||||
|
// If there are parameters to transform, but cannot store them, it is an error
|
||||||
|
if (node.Parameters.Count != 0 && pars == null)
|
||||||
|
throw new InvalidOperationException(string.Format("The parameters in this command '{0}' cannot be added to a null collection.", node.Parameters));
|
||||||
|
|
||||||
|
// Copy parameters to new comand
|
||||||
|
foreach (var parameter in node.Parameters)
|
||||||
|
pars.Add(parameter.Key, parameter.Value);
|
||||||
|
|
||||||
|
return string.Format("({0})", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseArgument(DynamicParser.Node.Argument node, bool isMultiPart = true, bool isOwner = false)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(node.Name) && (isOwner || (isMultiPart && IsTableAlias(node.Name))))
|
||||||
|
return node.Name;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseGetMember(DynamicParser.Node.GetMember node, IDictionary<string, IParameter> pars = null, bool decorate = true, bool isMultiPart = true, DynamicSchemaColumn? columnSchema = null)
|
||||||
|
{
|
||||||
|
if (node.Host is DynamicParser.Node.Argument && IsTableAlias(node.Name))
|
||||||
|
{
|
||||||
|
decorate = false;
|
||||||
|
isMultiPart = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This hack allows to use argument as alias, but when it is not nesesary use other column.
|
||||||
|
// Let say we hace a table Users with alias usr, and we join to table with alias ua which also has a column Users
|
||||||
|
// This allow use of usr => usr.ua.Users to result in ua."Users" instead of "Users" or usr."ua"."Users", se tests for examples.
|
||||||
|
string parent = null;
|
||||||
|
if (node.Host != null)
|
||||||
|
{
|
||||||
|
if (isMultiPart && node.Host is DynamicParser.Node.GetMember && IsTable(node.Host.Name, null))
|
||||||
|
{
|
||||||
|
if (node.Host.Host != null && node.Host.Host is DynamicParser.Node.GetMember && IsTable(node.Host.Name, node.Host.Host.Name))
|
||||||
|
parent = string.Format("{0}.{1}", Parse(node.Host.Host, pars, isMultiPart: false), Parse(node.Host, pars, isMultiPart: false));
|
||||||
|
else
|
||||||
|
parent = Parse(node.Host, pars, isMultiPart: false);
|
||||||
|
}
|
||||||
|
else if (isMultiPart)
|
||||||
|
parent = Parse(node.Host, pars, isMultiPart: isMultiPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
////string parent = node.Host == null || !isMultiPart ? null : Parse(node.Host, pars, isMultiPart: !IsTable(node.Name, node.Host.Name));
|
||||||
|
string name = parent == null ?
|
||||||
|
decorate ? Database.DecorateName(node.Name) : node.Name :
|
||||||
|
string.Format("{0}.{1}", parent, decorate ? Database.DecorateName(node.Name) : node.Name);
|
||||||
|
|
||||||
|
columnSchema = GetColumnFromSchema(name);
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseSetMember(DynamicParser.Node.SetMember node, IDictionary<string, IParameter> pars = null, bool decorate = true, bool isMultiPart = true, DynamicSchemaColumn? columnSchema = null)
|
||||||
|
{
|
||||||
|
if (node.Host is DynamicParser.Node.Argument && IsTableAlias(node.Name))
|
||||||
|
{
|
||||||
|
decorate = false;
|
||||||
|
isMultiPart = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string parent = null;
|
||||||
|
if (node.Host != null)
|
||||||
|
{
|
||||||
|
if (isMultiPart && node.Host is DynamicParser.Node.GetMember && IsTable(node.Host.Name, null))
|
||||||
|
{
|
||||||
|
if (node.Host.Host != null && node.Host.Host is DynamicParser.Node.GetMember && IsTable(node.Name, node.Host.Name))
|
||||||
|
parent = string.Format("{0}.{1}", Parse(node.Host.Host, pars, isMultiPart: false), Parse(node.Host, pars, isMultiPart: false));
|
||||||
|
else
|
||||||
|
parent = Parse(node.Host, pars, isMultiPart: false);
|
||||||
|
}
|
||||||
|
else if (isMultiPart)
|
||||||
|
parent = Parse(node.Host, pars, isMultiPart: isMultiPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
////string parent = node.Host == null || !isMultiPart ? null : Parse(node.Host, pars, isMultiPart: !IsTable(node.Name, node.Host.Name));
|
||||||
|
string name = parent == null ?
|
||||||
|
decorate ? Database.DecorateName(node.Name) : node.Name :
|
||||||
|
string.Format("{0}.{1}", parent, decorate ? Database.DecorateName(node.Name) : node.Name);
|
||||||
|
|
||||||
|
columnSchema = GetColumnFromSchema(name);
|
||||||
|
|
||||||
|
string value = Parse(node.Value, pars, nulls: true, columnSchema: columnSchema);
|
||||||
|
return string.Format("{0} = ({1})", name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseUnary(DynamicParser.Node.Unary node, IDictionary<string, IParameter> pars = null)
|
||||||
|
{
|
||||||
|
switch (node.Operation)
|
||||||
|
{
|
||||||
|
// Artifacts from the DynamicParser class that are not usefull here...
|
||||||
|
case ExpressionType.IsFalse:
|
||||||
|
case ExpressionType.IsTrue: return Parse(node.Target, pars);
|
||||||
|
|
||||||
|
// Unary supported operations...
|
||||||
|
case ExpressionType.Not: return string.Format("(NOT {0})", Parse(node.Target, pars));
|
||||||
|
case ExpressionType.Negate: return string.Format("!({0})", Parse(node.Target, pars));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException("Not supported unary operation: " + node);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseBinary(DynamicParser.Node.Binary node, IDictionary<string, IParameter> pars = null)
|
||||||
|
{
|
||||||
|
string op = string.Empty;
|
||||||
|
|
||||||
|
switch (node.Operation)
|
||||||
|
{
|
||||||
|
// Arithmetic binary operations...
|
||||||
|
case ExpressionType.Add: op = "+"; break;
|
||||||
|
case ExpressionType.Subtract: op = "-"; break;
|
||||||
|
case ExpressionType.Multiply: op = "*"; break;
|
||||||
|
case ExpressionType.Divide: op = "/"; break;
|
||||||
|
case ExpressionType.Modulo: op = "%"; break;
|
||||||
|
case ExpressionType.Power: op = "^"; break;
|
||||||
|
|
||||||
|
case ExpressionType.And: op = "AND"; break;
|
||||||
|
case ExpressionType.Or: op = "OR"; break;
|
||||||
|
|
||||||
|
// Logical comparisons...
|
||||||
|
case ExpressionType.GreaterThan: op = ">"; break;
|
||||||
|
case ExpressionType.GreaterThanOrEqual: op = ">="; break;
|
||||||
|
case ExpressionType.LessThan: op = "<"; break;
|
||||||
|
case ExpressionType.LessThanOrEqual: op = "<="; break;
|
||||||
|
|
||||||
|
// Comparisons against 'NULL' require the 'IS' or 'IS NOT' operator instead the numeric ones...
|
||||||
|
case ExpressionType.Equal: op = node.Right == null && !VirtualMode ? "IS" : "="; break;
|
||||||
|
case ExpressionType.NotEqual: op = node.Right == null && !VirtualMode ? "IS NOT" : "<>"; break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException("Not supported operator: '" + node.Operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicSchemaColumn? columnSchema = null;
|
||||||
|
string left = Parse(node.Left, pars, columnSchema: columnSchema); // Not nulls: left is assumed to be an object
|
||||||
|
string right = Parse(node.Right, pars, nulls: true, columnSchema: columnSchema);
|
||||||
|
return string.Format("({0} {1} {2})", left, op, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseMethod(DynamicParser.Node.Method node, IDictionary<string, IParameter> pars = null)
|
||||||
|
{
|
||||||
|
string method = node.Name.ToUpper();
|
||||||
|
string parent = node.Host == null ? null : Parse(node.Host, pars: pars);
|
||||||
|
string item = null;
|
||||||
|
|
||||||
|
// Root-level methods...
|
||||||
|
if (node.Host == null)
|
||||||
|
{
|
||||||
|
switch (method)
|
||||||
|
{
|
||||||
|
case "NOT":
|
||||||
|
if (node.Arguments == null || node.Arguments.Length != 1) throw new ArgumentNullException("NOT method expects one argument: " + node.Arguments.Sketch());
|
||||||
|
item = Parse(node.Arguments[0], pars: pars);
|
||||||
|
return string.Format("(NOT {0})", item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Column-level methods...
|
||||||
|
if (node.Host != null)
|
||||||
|
{
|
||||||
|
switch (method)
|
||||||
|
{
|
||||||
|
case "BETWEEN":
|
||||||
|
{
|
||||||
|
if (node.Arguments == null || node.Arguments.Length == 0)
|
||||||
|
throw new ArgumentException("BETWEEN method expects at least one argument: " + node.Arguments.Sketch());
|
||||||
|
|
||||||
|
if (node.Arguments.Length > 2)
|
||||||
|
throw new ArgumentException("BETWEEN method expects at most two arguments: " + node.Arguments.Sketch());
|
||||||
|
|
||||||
|
var arguments = node.Arguments;
|
||||||
|
|
||||||
|
if (arguments.Length == 1 && (arguments[0] is IEnumerable<object> || arguments[0] is Array) && !(arguments[0] is byte[]))
|
||||||
|
{
|
||||||
|
var vals = arguments[0] as IEnumerable<object>;
|
||||||
|
|
||||||
|
if (vals == null && arguments[0] is Array)
|
||||||
|
vals = ((Array)arguments[0]).Cast<object>() as IEnumerable<object>;
|
||||||
|
|
||||||
|
if (vals != null)
|
||||||
|
arguments = vals.ToArray();
|
||||||
|
else
|
||||||
|
throw new ArgumentException("BETWEEN method expects single argument to be enumerable of exactly two elements: " + node.Arguments.Sketch());
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Format("{0} BETWEEN {1} AND {2}", parent, Parse(arguments[0], pars: pars), Parse(arguments[1], pars: pars));
|
||||||
|
}
|
||||||
|
|
||||||
|
case "IN":
|
||||||
|
{
|
||||||
|
if (node.Arguments == null || node.Arguments.Length == 0)
|
||||||
|
throw new ArgumentException("IN method expects at least one argument: " + node.Arguments.Sketch());
|
||||||
|
|
||||||
|
bool firstParam = true;
|
||||||
|
StringBuilder sbin = new StringBuilder();
|
||||||
|
foreach (var arg in node.Arguments)
|
||||||
|
{
|
||||||
|
if (!firstParam)
|
||||||
|
sbin.Append(", ");
|
||||||
|
|
||||||
|
if ((arg is IEnumerable<object> || arg is Array) && !(arg is byte[]))
|
||||||
|
{
|
||||||
|
var vals = arg as IEnumerable<object>;
|
||||||
|
|
||||||
|
if (vals == null && arg is Array)
|
||||||
|
vals = ((Array)arg).Cast<object>() as IEnumerable<object>;
|
||||||
|
|
||||||
|
if (vals != null)
|
||||||
|
foreach (var val in vals)
|
||||||
|
{
|
||||||
|
if (!firstParam)
|
||||||
|
sbin.Append(", ");
|
||||||
|
else
|
||||||
|
firstParam = false;
|
||||||
|
|
||||||
|
sbin.Append(Parse(val, pars: pars));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sbin.Append(Parse(arg, pars: pars));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sbin.Append(Parse(arg, pars: pars));
|
||||||
|
|
||||||
|
firstParam = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Format("{0} IN({1})", parent, sbin.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
case "LIKE":
|
||||||
|
if (node.Arguments == null || node.Arguments.Length != 1)
|
||||||
|
throw new ArgumentException("LIKE method expects one argument: " + node.Arguments.Sketch());
|
||||||
|
|
||||||
|
return string.Format("{0} LIKE {1}", parent, Parse(node.Arguments[0], pars: pars));
|
||||||
|
|
||||||
|
case "NOTLIKE":
|
||||||
|
if (node.Arguments == null || node.Arguments.Length != 1)
|
||||||
|
throw new ArgumentException("NOT LIKE method expects one argument: " + node.Arguments.Sketch());
|
||||||
|
|
||||||
|
return string.Format("{0} NOT LIKE {1}", parent, Parse(node.Arguments[0], pars: pars));
|
||||||
|
|
||||||
|
case "AS":
|
||||||
|
if (node.Arguments == null || node.Arguments.Length != 1)
|
||||||
|
throw new ArgumentException("AS method expects one argument: " + node.Arguments.Sketch());
|
||||||
|
|
||||||
|
item = Parse(node.Arguments[0], pars: null, rawstr: true, isMultiPart: false); // pars=null to avoid to parameterize aliases
|
||||||
|
item = item.Validated("Alias"); // Intercepting null and empty aliases
|
||||||
|
return string.Format("{0} AS {1}", parent, item);
|
||||||
|
|
||||||
|
case "COUNT":
|
||||||
|
if (node.Arguments != null && node.Arguments.Length > 1)
|
||||||
|
throw new ArgumentException("COUNT method expects one or none argument: " + node.Arguments.Sketch());
|
||||||
|
|
||||||
|
if (node.Arguments == null || node.Arguments.Length == 0)
|
||||||
|
return "COUNT(*)";
|
||||||
|
|
||||||
|
return string.Format("COUNT({0})", Parse(node.Arguments[0], pars: Parameters, nulls: true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default case: parsing the method's name along with its arguments...
|
||||||
|
method = parent == null ? node.Name : string.Format("{0}.{1}", parent, node.Name);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendFormat("{0}(", method);
|
||||||
|
|
||||||
|
if (node.Arguments != null && node.Arguments.Length != 0)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
foreach (object argument in node.Arguments)
|
||||||
|
{
|
||||||
|
if (!first)
|
||||||
|
sb.Append(", ");
|
||||||
|
else
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
sb.Append(Parse(argument, pars, nulls: true)); // We don't accept raw strings here!!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append(")");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseInvoke(DynamicParser.Node.Invoke node, IDictionary<string, IParameter> pars = null)
|
||||||
|
{
|
||||||
|
// This is used as an especial syntax to merely concatenate its arguments. It is used as a way to extend the supported syntax without the need of treating all the possible cases...
|
||||||
|
if (node.Arguments == null || node.Arguments.Length == 0)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
foreach (object arg in node.Arguments)
|
||||||
|
{
|
||||||
|
if (arg is string)
|
||||||
|
sb.Append((string)arg);
|
||||||
|
else
|
||||||
|
sb.Append(Parse(arg, pars, rawstr: true, nulls: true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseConvert(DynamicParser.Node.Convert node, IDictionary<string, IParameter> pars = null)
|
||||||
|
{
|
||||||
|
// The cast mechanism is left for the specific database implementation, that should override this method
|
||||||
|
// as needed...
|
||||||
|
string r = Parse(node.Target, pars);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseConstant(object node, IDictionary<string, IParameter> pars = null, DynamicSchemaColumn? columnSchema = null)
|
||||||
|
{
|
||||||
|
if (node == null) return ParseNull();
|
||||||
|
|
||||||
|
if (pars != null)
|
||||||
|
{
|
||||||
|
// If we have a list of parameters to store it, let's parametrize it
|
||||||
|
var name = Guid.NewGuid().ToString();
|
||||||
|
var par = new Parameter()
|
||||||
|
{
|
||||||
|
Name = string.Format("[${0}]", name),
|
||||||
|
Value = node,
|
||||||
|
Virtual = VirtualMode,
|
||||||
|
Schema = columnSchema,
|
||||||
|
};
|
||||||
|
|
||||||
|
pars.Add(name, par);
|
||||||
|
|
||||||
|
return par.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.ToString(); // Last resort case
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string ParseNull()
|
||||||
|
{
|
||||||
|
return "NULL"; // Override if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string FixObjectName(string main, bool onlyColumn = false)
|
||||||
|
{
|
||||||
|
if (main.IndexOf("(") > 0 && main.IndexOf(")") > 0)
|
||||||
|
return main.FillStringWithVariables(f => string.Format("({0})", FixObjectNamePrivate(f, onlyColumn)), "(", ")");
|
||||||
|
else
|
||||||
|
return FixObjectNamePrivate(main, onlyColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FixObjectNamePrivate(string f, bool onlyColumn = false)
|
||||||
|
{
|
||||||
|
var objects = f.Split('.')
|
||||||
|
.Select(x => Database.StripName(x));
|
||||||
|
|
||||||
|
if (onlyColumn || objects.Count() == 1)
|
||||||
|
f = Database.DecorateName(objects.Last());
|
||||||
|
else if (!IsTableAlias(objects.First()))
|
||||||
|
f = string.Join(".", objects.Select(o => Database.DecorateName(o)));
|
||||||
|
else
|
||||||
|
f = string.Format("{0}.{1}", objects.First(), string.Join(".", objects.Skip(1).Select(o => Database.DecorateName(o))));
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Parser
|
||||||
|
}
|
||||||
|
}
|
||||||
1238
DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs
Normal file
1238
DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs
Normal file
File diff suppressed because it is too large
Load Diff
324
DynamORM/Builders/Implementation/DynamicUpdateQueryBuilder.cs
Normal file
324
DynamORM/Builders/Implementation/DynamicUpdateQueryBuilder.cs
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Some of methods in this code file is based on Kerosene ORM solution
|
||||||
|
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using DynamORM.Builders.Extensions;
|
||||||
|
using DynamORM.Helpers.Dynamics;
|
||||||
|
using DynamORM.Mapper;
|
||||||
|
|
||||||
|
namespace DynamORM.Builders.Implementation
|
||||||
|
{
|
||||||
|
/// <summary>Update query builder.</summary>
|
||||||
|
internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
|
{
|
||||||
|
private string _columns;
|
||||||
|
|
||||||
|
internal DynamicUpdateQueryBuilder(DynamicDatabase db)
|
||||||
|
: base(db)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicUpdateQueryBuilder(DynamicDatabase db, string tableName)
|
||||||
|
: base(db, tableName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Generates the text this command will execute against the underlying database.</summary>
|
||||||
|
/// <returns>The text to execute against the underlying database.</returns>
|
||||||
|
/// <remarks>This method must be override by derived classes.</remarks>
|
||||||
|
public override string CommandText()
|
||||||
|
{
|
||||||
|
var info = Tables.Single();
|
||||||
|
return string.Format("UPDATE {0}{1} SET {2} WHERE {3}",
|
||||||
|
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
||||||
|
Database.DecorateName(info.Name), _columns, WhereCondition);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Update
|
||||||
|
|
||||||
|
/// <summary>Add update value or where condition using schema.</summary>
|
||||||
|
/// <param name="column">Update or where column name and value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Update(DynamicColumn column)
|
||||||
|
{
|
||||||
|
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
|
||||||
|
|
||||||
|
if (!col.HasValue && SupportSchema)
|
||||||
|
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column));
|
||||||
|
|
||||||
|
if (col.HasValue && col.Value.IsKey)
|
||||||
|
Where(column);
|
||||||
|
else
|
||||||
|
Values(column.ColumnName, column.Value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add update value or where condition using schema.</summary>
|
||||||
|
/// <param name="column">Update or where column name.</param>
|
||||||
|
/// <param name="value">Column value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Update(string column, object value)
|
||||||
|
{
|
||||||
|
DynamicSchemaColumn? col = GetColumnFromSchema(column);
|
||||||
|
|
||||||
|
if (!col.HasValue && SupportSchema)
|
||||||
|
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column));
|
||||||
|
|
||||||
|
if (col.HasValue && col.Value.IsKey)
|
||||||
|
Where(column, value);
|
||||||
|
else
|
||||||
|
Values(column, value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add update values and where condition columns using schema.</summary>
|
||||||
|
/// <param name="conditions">Set values or conditions as properties and values of an object.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Update(object conditions)
|
||||||
|
{
|
||||||
|
if (conditions is DynamicColumn)
|
||||||
|
return Update((DynamicColumn)conditions);
|
||||||
|
|
||||||
|
var dict = conditions.ToDictionary();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(conditions.GetType());
|
||||||
|
|
||||||
|
foreach (var con in dict)
|
||||||
|
{
|
||||||
|
if (mapper.Ignored.Contains(con.Key))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string colName = mapper != null ? mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key : con.Key;
|
||||||
|
DynamicSchemaColumn? col = GetColumnFromSchema(colName);
|
||||||
|
|
||||||
|
if (!col.HasValue && SupportSchema)
|
||||||
|
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", colName));
|
||||||
|
|
||||||
|
if (col.HasValue)
|
||||||
|
{
|
||||||
|
colName = col.Value.Name;
|
||||||
|
|
||||||
|
if (col.Value.IsKey)
|
||||||
|
{
|
||||||
|
Where(colName, con.Value);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Values(colName, con.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Update
|
||||||
|
|
||||||
|
#region Values
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the columns to update using the dynamic lambda expressions given. Each expression correspond to one
|
||||||
|
/// column, and can:
|
||||||
|
/// <para>- Resolve to a string, in this case a '=' must appear in the string.</para>
|
||||||
|
/// <para>- Resolve to a expression with the form: 'x => x.Column = Value'.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The specifications.</param>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Values(params Func<dynamic, object>[] func)
|
||||||
|
{
|
||||||
|
if (func == null)
|
||||||
|
throw new ArgumentNullException("Array of specifications cannot be null.");
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
foreach (var f in func)
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
if (f == null)
|
||||||
|
throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index));
|
||||||
|
var result = DynamicParser.Parse(f).Result;
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index));
|
||||||
|
|
||||||
|
string main = null;
|
||||||
|
string value = null;
|
||||||
|
string str = null;
|
||||||
|
|
||||||
|
// When 'x => x.Table.Column = value' or 'x => x.Column = value'...
|
||||||
|
if (result is DynamicParser.Node.SetMember)
|
||||||
|
{
|
||||||
|
var node = (DynamicParser.Node.SetMember)result;
|
||||||
|
|
||||||
|
DynamicSchemaColumn? col = GetColumnFromSchema(node.Name);
|
||||||
|
main = Database.DecorateName(node.Name);
|
||||||
|
value = Parse(node.Value, pars: Parameters, nulls: true, columnSchema: col);
|
||||||
|
|
||||||
|
str = string.Format("{0} = {1}", main, value);
|
||||||
|
_columns = _columns == null ? str : string.Format("{0}, {1}", _columns, str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType)
|
||||||
|
{
|
||||||
|
Values(result);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other specifications are considered invalid...
|
||||||
|
var err = string.Format("Specification '{0}' is invalid.", result);
|
||||||
|
str = Parse(result);
|
||||||
|
if (str.Contains("=")) err += " May have you used a '==' instead of a '=' operator?";
|
||||||
|
throw new ArgumentException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add insert fields.</summary>
|
||||||
|
/// <param name="column">Insert column and value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Values(DynamicColumn column)
|
||||||
|
{
|
||||||
|
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
|
||||||
|
|
||||||
|
string main = FixObjectName(column.ColumnName, onlyColumn: true);
|
||||||
|
string value = Parse(column.Value, pars: Parameters, nulls: true, columnSchema: col);
|
||||||
|
|
||||||
|
var str = string.Format("{0} = {1}", main, value);
|
||||||
|
_columns = _columns == null ? str : string.Format("{0}, {1}", _columns, str);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add insert fields.</summary>
|
||||||
|
/// <param name="column">Insert column.</param>
|
||||||
|
/// <param name="value">Insert value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Values(string column, object value)
|
||||||
|
{
|
||||||
|
if (value is DynamicColumn)
|
||||||
|
{
|
||||||
|
var v = (DynamicColumn)value;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(v.ColumnName))
|
||||||
|
v.ColumnName = column;
|
||||||
|
|
||||||
|
return Values(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Values(new DynamicColumn
|
||||||
|
{
|
||||||
|
ColumnName = column,
|
||||||
|
Value = value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add insert fields.</summary>
|
||||||
|
/// <param name="o">Set insert value as properties and values of an object.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Values(object o)
|
||||||
|
{
|
||||||
|
var dict = o.ToDictionary();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(o.GetType());
|
||||||
|
|
||||||
|
if (mapper != null)
|
||||||
|
{
|
||||||
|
foreach (var con in dict)
|
||||||
|
if (!mapper.Ignored.Contains(con.Key))
|
||||||
|
Values(mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key, con.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
foreach (var con in dict)
|
||||||
|
Values(con.Key, con.Value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Values
|
||||||
|
|
||||||
|
#region Where
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||||
|
/// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used
|
||||||
|
/// as needed.
|
||||||
|
/// <para>- If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||||
|
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||||
|
/// 'Where( x => x.Or( condition ) )'.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The specification.</param>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Where(Func<dynamic, object> func)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add where condition.</summary>
|
||||||
|
/// <param name="column">Condition column with operator and value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Where(DynamicColumn column)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add where condition.</summary>
|
||||||
|
/// <param name="column">Condition column.</param>
|
||||||
|
/// <param name="op">Condition operator.</param>
|
||||||
|
/// <param name="value">Condition value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(column, op, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add where condition.</summary>
|
||||||
|
/// <param name="column">Condition column.</param>
|
||||||
|
/// <param name="value">Condition value.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Where(string column, object value)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(column, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add where condition.</summary>
|
||||||
|
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||||
|
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||||
|
/// aren't keys.</param>
|
||||||
|
/// <returns>Builder instance.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Where(object conditions, bool schema = false)
|
||||||
|
{
|
||||||
|
return this.InternalWhere(conditions, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Where
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user