diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs
index 39740b5..212eb91 100644
--- a/AmalgamationTool/DynamORM.Amalgamation.cs
+++ b/AmalgamationTool/DynamORM.Amalgamation.cs
@@ -10,6 +10,7 @@ using DynamORM.Builders;
using DynamORM.Helpers.Dynamics;
using DynamORM.Helpers;
using DynamORM.Mapper;
+using DynamORM.TypedSql;
using DynamORM.Validation;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -7203,6 +7204,9 @@ namespace DynamORM
/// Predicate to parse.
/// Builder instance.
IDynamicTypedDeleteQueryBuilder Where(Expression> predicate);
+
+ /// Add typed SQL DSL where predicate.
+ IDynamicTypedDeleteQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate);
}
/// Typed insert query builder for mapped entities.
/// Mapped entity type.
@@ -7219,6 +7223,9 @@ namespace DynamORM
/// Mapped object value.
/// Builder instance.
IDynamicTypedInsertQueryBuilder Insert(T value);
+
+ /// Add typed SQL DSL insert assignment.
+ IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory);
}
/// Typed select query builder for mapped entities.
/// Mapped entity type.
@@ -7278,6 +7285,21 @@ namespace DynamORM
/// Additional selectors to parse.
/// Builder instance.
IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors);
+
+ /// Add typed SQL DSL select items.
+ IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors);
+
+ /// Add typed SQL DSL where predicate.
+ IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate);
+
+ /// Add typed SQL DSL having predicate.
+ IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate);
+
+ /// Add typed SQL DSL group by expressions.
+ IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors);
+
+ /// Add typed SQL DSL order by expressions.
+ IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors);
}
/// Typed update query builder for mapped entities.
/// Mapped entity type.
@@ -7299,6 +7321,12 @@ namespace DynamORM
/// Mapped object value.
/// Builder instance.
IDynamicTypedUpdateQueryBuilder Values(T value);
+
+ /// Add typed SQL DSL where predicate.
+ IDynamicTypedUpdateQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate);
+
+ /// Add typed SQL DSL assignment.
+ IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory);
}
/// Dynamic update query builder interface.
/// This interface it publicly available. Implementation should be hidden.
@@ -7475,6 +7503,9 @@ namespace DynamORM
/// Gets raw ON condition.
public string OnRawCondition { get; private set; }
+ /// Gets typed SQL DSL ON specification.
+ public Func, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; }
+
/// Sets join alias.
public TypedJoinBuilder As(string alias)
{
@@ -7560,6 +7591,7 @@ namespace DynamORM
OnPredicate = predicate;
OnRawCondition = null;
+ OnSqlPredicate = null;
return this;
}
/// Sets raw ON clause (without the ON keyword).
@@ -7570,6 +7602,18 @@ namespace DynamORM
OnRawCondition = condition.Trim();
OnPredicate = null;
+ OnSqlPredicate = null;
+ return this;
+ }
+ /// Sets typed SQL DSL ON predicate.
+ public TypedJoinBuilder OnSql(Func, TypedTableContext, TypedSqlPredicate> predicate)
+ {
+ if (predicate == null)
+ throw new ArgumentNullException("predicate");
+
+ OnSqlPredicate = predicate;
+ OnPredicate = null;
+ OnRawCondition = null;
return this;
}
}
@@ -8229,8 +8273,8 @@ namespace DynamORM
/// Implementation of dynamic insert query builder.
internal class DynamicInsertQueryBuilder : DynamicModifyBuilder, IDynamicInsertQueryBuilder
{
- private string _columns;
- private string _values;
+ protected string _columns;
+ protected string _values;
///
/// Initializes a new instance of the class.
@@ -9338,11 +9382,11 @@ namespace DynamORM
private int? _offset = null;
private bool _distinct = false;
- private string _select;
+ protected string _select;
private string _from;
protected string _join;
- private string _groupby;
- private string _orderby;
+ protected string _groupby;
+ protected string _orderby;
#region IQueryWithHaving
@@ -10678,6 +10722,16 @@ namespace DynamORM
TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate);
return this;
}
+ public IDynamicTypedDeleteQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate)
+ {
+ string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName);
+ if (string.IsNullOrEmpty(WhereCondition))
+ WhereCondition = condition;
+ else
+ WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition);
+
+ return this;
+ }
public new IDynamicTypedDeleteQueryBuilder Where(Func func)
{
base.Where(func);
@@ -10703,6 +10757,14 @@ namespace DynamORM
base.Where(conditions, schema);
return this;
}
+ private string RenderValue(object value)
+ {
+ if (value == null)
+ return "NULL";
+
+ DynamicSchemaColumn? columnSchema = null;
+ return ParseConstant(value, Parameters, columnSchema);
+ }
}
/// Typed wrapper over with property-to-column translation.
/// Mapped entity type.
@@ -10730,6 +10792,15 @@ namespace DynamORM
base.Insert(value);
return this;
}
+ public IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory)
+ {
+ string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true);
+ string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName);
+
+ _columns = _columns == null ? column : string.Format("{0}, {1}", _columns, column);
+ _values = _values == null ? value : string.Format("{0}, {1}", _values, value);
+ return this;
+ }
public new IDynamicTypedInsertQueryBuilder Values(Func fn, params Func[] func)
{
base.Values(fn, func);
@@ -10745,11 +10816,54 @@ namespace DynamORM
base.Insert(o);
return this;
}
+ private string RenderValue(object value)
+ {
+ if (value == null)
+ return "NULL";
+
+ DynamicSchemaColumn? columnSchema = null;
+ return ParseConstant(value, Parameters, columnSchema);
+ }
}
/// Typed wrapper over with property-to-column translation.
/// Mapped entity type.
internal class DynamicTypedSelectQueryBuilder : DynamicSelectQueryBuilder, IDynamicTypedSelectQueryBuilder
{
+ private sealed class TypedSqlRenderContext : ITypedSqlRenderContext
+ {
+ private readonly DynamicTypedSelectQueryBuilder _builder;
+
+ public TypedSqlRenderContext(DynamicTypedSelectQueryBuilder builder)
+ {
+ _builder = builder;
+ }
+ public string ResolveColumn(Type modelType, string memberName, string alias)
+ {
+ DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType);
+ if (mapper == null)
+ throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName));
+
+ string mappedColumn = mapper.PropertyMap.TryGetValue(memberName)
+ ?? mapper.PropertyMap.Where(x => string.Equals(x.Key, memberName, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).FirstOrDefault()
+ ?? memberName;
+
+ return string.IsNullOrEmpty(alias)
+ ? _builder.Database.DecorateName(mappedColumn)
+ : string.Format("{0}.{1}", alias, _builder.Database.DecorateName(mappedColumn));
+ }
+ public string RenderValue(object value)
+ {
+ if (value == null)
+ return "NULL";
+
+ DynamicSchemaColumn? columnSchema = null;
+ return _builder.ParseConstant(value, _builder.Parameters, columnSchema);
+ }
+ public string DecorateName(string name)
+ {
+ return _builder.Database.DecorateName(name);
+ }
+ }
private readonly DynamicTypeMap _mapper;
public DynamicTypedSelectQueryBuilder(DynamicDatabase db)
@@ -10846,7 +10960,12 @@ namespace DynamORM
if (SupportNoLock && spec.UseNoLock)
joinExpr += " WITH(NOLOCK)";
- if (!string.IsNullOrEmpty(spec.OnRawCondition))
+ if (spec.OnSqlPredicate != null)
+ {
+ TypedSqlRenderContext context = new TypedSqlRenderContext(this);
+ joinExpr += string.Format(" ON {0}", spec.OnSqlPredicate(new TypedTableContext(GetRootAliasOrTableName()), new TypedTableContext(rightAlias)).Render(context));
+ }
+ else if (!string.IsNullOrEmpty(spec.OnRawCondition))
joinExpr += string.Format(" ON {0}", spec.OnRawCondition);
AppendJoinClause(joinExpr);
@@ -10894,6 +11013,18 @@ namespace DynamORM
}
return this;
}
+ public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors)
+ {
+ if (selector == null)
+ throw new ArgumentNullException("selector");
+
+ AddSelectSqlSelector(selector);
+ if (selectors != null)
+ foreach (Func, TypedSqlSelectable> item in selectors)
+ AddSelectSqlSelector(item);
+
+ return this;
+ }
public IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors)
{
if (selector == null)
@@ -10911,6 +11042,18 @@ namespace DynamORM
}
return this;
}
+ public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors)
+ {
+ if (selector == null)
+ throw new ArgumentNullException("selector");
+
+ AddGroupBySqlSelector(selector);
+ if (selectors != null)
+ foreach (Func, TypedSqlExpression> item in selectors)
+ AddGroupBySqlSelector(item);
+
+ return this;
+ }
public IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors)
{
if (selector == null)
@@ -10928,6 +11071,18 @@ namespace DynamORM
}
return this;
}
+ public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors)
+ {
+ if (selector == null)
+ throw new ArgumentNullException("selector");
+
+ AddOrderBySqlSelector(selector);
+ if (selectors != null)
+ foreach (Func, TypedSqlOrderExpression> item in selectors)
+ AddOrderBySqlSelector(item);
+
+ return this;
+ }
public new IDynamicTypedSelectQueryBuilder Top(int? top)
{
base.Top(top);
@@ -10962,6 +11117,32 @@ namespace DynamORM
return this;
}
+ public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate)
+ {
+ if (predicate == null)
+ throw new ArgumentNullException("predicate");
+
+ string condition = RenderSqlPredicate(predicate);
+ if (string.IsNullOrEmpty(WhereCondition))
+ WhereCondition = condition;
+ else
+ WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition);
+
+ return this;
+ }
+ public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate)
+ {
+ if (predicate == null)
+ throw new ArgumentNullException("predicate");
+
+ string condition = RenderSqlPredicate(predicate);
+ if (string.IsNullOrEmpty(HavingCondition))
+ HavingCondition = condition;
+ else
+ HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition);
+
+ return this;
+ }
public new IDynamicTypedSelectQueryBuilder Having(DynamicColumn column)
{
base.Having(column);
@@ -11000,6 +11181,16 @@ namespace DynamORM
((IDynamicSelectQueryBuilder)this).Select(x => parsed);
}
}
+ private void AddSelectSqlSelector(Func, TypedSqlSelectable> selector)
+ {
+ if (selector == null)
+ throw new ArgumentNullException("selector");
+
+ TypedSqlRenderContext context = new TypedSqlRenderContext(this);
+ TypedSqlSelectable item = selector(new TypedTableContext(GetRootAliasOrTableName()));
+ string rendered = item.Render(context);
+ _select = string.IsNullOrEmpty(_select) ? rendered : string.Format("{0}, {1}", _select, rendered);
+ }
private void AddGroupBySelector(Expression> selector)
{
var body = UnwrapConvert(selector.Body);
@@ -11018,6 +11209,16 @@ namespace DynamORM
((IDynamicSelectQueryBuilder)this).GroupBy(x => parsed);
}
}
+ private void AddGroupBySqlSelector(Func, TypedSqlExpression> selector)
+ {
+ if (selector == null)
+ throw new ArgumentNullException("selector");
+
+ TypedSqlRenderContext context = new TypedSqlRenderContext(this);
+ TypedSqlExpression item = selector(new TypedTableContext(GetRootAliasOrTableName()));
+ string rendered = item.Render(context);
+ _groupby = string.IsNullOrEmpty(_groupby) ? rendered : string.Format("{0}, {1}", _groupby, rendered);
+ }
private void AddOrderBySelector(Expression> selector)
{
var body = UnwrapConvert(selector.Body);
@@ -11032,6 +11233,21 @@ namespace DynamORM
string parsed = string.Format("{0} {1}", main, ascending ? "ASC" : "DESC");
((IDynamicSelectQueryBuilder)this).OrderBy(x => parsed);
}
+ private void AddOrderBySqlSelector(Func, TypedSqlOrderExpression> selector)
+ {
+ if (selector == null)
+ throw new ArgumentNullException("selector");
+
+ TypedSqlRenderContext context = new TypedSqlRenderContext(this);
+ TypedSqlOrderExpression item = selector(new TypedTableContext(GetRootAliasOrTableName()));
+ string rendered = item.Render(context);
+ _orderby = string.IsNullOrEmpty(_orderby) ? rendered : string.Format("{0}, {1}", _orderby, rendered);
+ }
+ private string RenderSqlPredicate(Func, TypedSqlPredicate> predicate)
+ {
+ TypedSqlRenderContext context = new TypedSqlRenderContext(this);
+ return predicate(new TypedTableContext(GetRootAliasOrTableName())).Render(context);
+ }
private string ParseTypedCondition(Expression expression)
{
expression = UnwrapConvert(expression);
@@ -11415,6 +11631,24 @@ namespace DynamORM
base.Values(value);
return this;
}
+ public IDynamicTypedUpdateQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate)
+ {
+ string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName);
+ if (string.IsNullOrEmpty(WhereCondition))
+ WhereCondition = condition;
+ else
+ WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition);
+
+ return this;
+ }
+ public IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory)
+ {
+ string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true);
+ string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName);
+ string assignment = string.Format("{0} = {1}", column, value);
+ _columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment);
+ return this;
+ }
public new IDynamicTypedUpdateQueryBuilder Update(string column, object value)
{
base.Update(column, value);
@@ -11465,11 +11699,19 @@ namespace DynamORM
base.Where(conditions, schema);
return this;
}
+ private string RenderValue(object value)
+ {
+ if (value == null)
+ return "NULL";
+
+ DynamicSchemaColumn? columnSchema = null;
+ return ParseConstant(value, Parameters, columnSchema);
+ }
}
/// Update query builder.
internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
{
- private string _columns;
+ protected string _columns;
internal DynamicUpdateQueryBuilder(DynamicDatabase db)
: base(db)
@@ -11749,6 +11991,31 @@ namespace DynamORM
/// Helper methods for typed modify builders.
internal static class TypedModifyHelper
{
+ private sealed class ModifyRenderContext : ITypedSqlRenderContext
+ {
+ private readonly Func, string> _resolveColumn;
+ private readonly Func