diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index ccef0e0..4f01c18 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -7526,135 +7526,6 @@ namespace DynamORM return builder.Join(on, alias, DynamicJoinType.Full); } } - /// Typed helper extensions for update/insert/delete fluent APIs. - public static class TypedModifyExtensions - { - /// Add typed where predicate (AND-composed comparisons) for update builder. - public static IDynamicUpdateQueryBuilder WhereTyped(this IDynamicUpdateQueryBuilder builder, Expression> predicate) - { - AddTypedWhere(builder, predicate == null ? null : predicate.Body); - return builder; - } - /// Add typed where predicate (AND-composed comparisons) for delete builder. - public static IDynamicDeleteQueryBuilder WhereTyped(this IDynamicDeleteQueryBuilder builder, Expression> predicate) - { - AddTypedWhere(builder, predicate == null ? null : predicate.Body); - return builder; - } - /// Add typed value assignment for update builder. - public static IDynamicUpdateQueryBuilder SetTyped(this IDynamicUpdateQueryBuilder builder, Expression> selector, object value) - { - string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); - return builder.Values(col, value); - } - /// Add typed value assignment for insert builder. - public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, Expression> selector, object value) - { - string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); - return builder.Insert(col, value); - } - /// Insert mapped object with compile-time type information. - public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, T value) where T : class - { - return builder.Insert(value); - } - /// Update mapped object with compile-time type information. - public static IDynamicUpdateQueryBuilder UpdateTyped(this IDynamicUpdateQueryBuilder builder, T value) where T : class - { - return builder.Update(value); - } - private static void AddTypedWhere(dynamic builder, Expression expression) - { - if (expression == null) - throw new ArgumentNullException("predicate"); - - expression = UnwrapConvert(expression); - BinaryExpression be = expression as BinaryExpression; - - if (be != null && (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And)) - { - AddTypedWhere(builder, be.Left); - AddTypedWhere(builder, be.Right); - return; - } - if (be != null) - { - string col = GetColumnName(GetRootParameterType(be.Left), be.Left); - object val = EvaluateExpression(be.Right); - - switch (be.NodeType) - { - case ExpressionType.Equal: - builder.Where(col, DynamicColumn.CompareOperator.Eq, val); - return; - case ExpressionType.NotEqual: - builder.Where(col, DynamicColumn.CompareOperator.Not, val); - return; - case ExpressionType.GreaterThan: - builder.Where(col, DynamicColumn.CompareOperator.Gt, val); - return; - case ExpressionType.GreaterThanOrEqual: - builder.Where(col, DynamicColumn.CompareOperator.Gte, val); - return; - case ExpressionType.LessThan: - builder.Where(col, DynamicColumn.CompareOperator.Lt, val); - return; - case ExpressionType.LessThanOrEqual: - builder.Where(col, DynamicColumn.CompareOperator.Lte, val); - return; - } - } - throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); - } - private static Type GetRootParameterType(Expression expression) - { - expression = UnwrapConvert(expression); - MemberExpression m = expression as MemberExpression; - if (m != null && m.Expression is ParameterExpression) - return ((ParameterExpression)m.Expression).Type; - - throw new NotSupportedException(string.Format("Unsupported typed selector: {0}", expression)); - } - private static string GetColumnName(Type modelType, Expression expression) - { - if (modelType == null) - modelType = GetRootParameterType(expression); - - expression = UnwrapConvert(expression); - MemberExpression member = expression as MemberExpression; - if (member == null) - throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); - - DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); - if (mapper == null) - throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); - - string col = mapper.PropertyMap.TryGetValue(member.Member.Name) - ?? mapper.PropertyMap - .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Value) - .FirstOrDefault() - ?? member.Member.Name; - - return col; - } - private static Expression UnwrapConvert(Expression expression) - { - while (expression is UnaryExpression && - (((UnaryExpression)expression).NodeType == ExpressionType.Convert || - ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) - expression = ((UnaryExpression)expression).Operand; - - return expression; - } - private static object EvaluateExpression(Expression expression) - { - expression = UnwrapConvert(expression); - var objectMember = Expression.Convert(expression, typeof(object)); - var getter = Expression.Lambda>(objectMember).Compile(); - return getter(); - } - } namespace Extensions { internal static class DynamicHavingQueryExtensions @@ -10679,7 +10550,7 @@ namespace DynamORM } public IDynamicTypedDeleteQueryBuilder Where(Expression> predicate) { - this.WhereTyped(predicate); + TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } public new IDynamicTypedDeleteQueryBuilder Where(Func func) @@ -10718,7 +10589,7 @@ namespace DynamORM } public IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value) { - this.InsertTyped(selector, value); + base.Insert(TypedModifyHelper.GetMappedColumn(selector), value); return this; } public IDynamicTypedInsertQueryBuilder Insert(T value) @@ -11290,12 +11161,12 @@ namespace DynamORM } public IDynamicTypedUpdateQueryBuilder Where(Expression> predicate) { - this.WhereTyped(predicate); + TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } public IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value) { - this.SetTyped(selector, value); + base.Values(TypedModifyHelper.GetMappedColumn(selector), value); return this; } public IDynamicTypedUpdateQueryBuilder Values(T value) @@ -11634,6 +11505,99 @@ namespace DynamORM } #endregion IExtendedDisposable } + /// Helper methods for typed modify builders. + internal static class TypedModifyHelper + { + public static string GetMappedColumn(Expression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + return GetMappedColumn(typeof(T), selector.Body); + } + public static void ApplyWhere(Action addCondition, Expression> predicate) + { + if (addCondition == null) + throw new ArgumentNullException("addCondition"); + if (predicate == null) + throw new ArgumentNullException("predicate"); + + ApplyWhereInternal(typeof(T), addCondition, predicate.Body); + } + private static void ApplyWhereInternal(Type modelType, Action addCondition, Expression expression) + { + expression = UnwrapConvert(expression); + BinaryExpression be = expression as BinaryExpression; + if (be == null) + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + + if (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And) + { + ApplyWhereInternal(modelType, addCondition, be.Left); + ApplyWhereInternal(modelType, addCondition, be.Right); + return; + } + string col = GetMappedColumn(modelType, be.Left); + object val = EvaluateExpression(be.Right); + + switch (be.NodeType) + { + case ExpressionType.Equal: + addCondition(col, DynamicColumn.CompareOperator.Eq, val); + return; + case ExpressionType.NotEqual: + addCondition(col, DynamicColumn.CompareOperator.Not, val); + return; + case ExpressionType.GreaterThan: + addCondition(col, DynamicColumn.CompareOperator.Gt, val); + return; + case ExpressionType.GreaterThanOrEqual: + addCondition(col, DynamicColumn.CompareOperator.Gte, val); + return; + case ExpressionType.LessThan: + addCondition(col, DynamicColumn.CompareOperator.Lt, val); + return; + case ExpressionType.LessThanOrEqual: + addCondition(col, DynamicColumn.CompareOperator.Lte, val); + return; + } + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + } + private static string GetMappedColumn(Type modelType, Expression expression) + { + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member == null) + throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); + + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + return mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + } + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression && + (((UnaryExpression)expression).NodeType == ExpressionType.Convert || + ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) + expression = ((UnaryExpression)expression).Operand; + + return expression; + } + private static object EvaluateExpression(Expression expression) + { + expression = UnwrapConvert(expression); + var objectMember = Expression.Convert(expression, typeof(object)); + var getter = Expression.Lambda>(objectMember).Compile(); + return getter(); + } + } } } namespace Helpers diff --git a/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs index 0fb768e..ac41e42 100644 --- a/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs +++ b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs @@ -32,21 +32,6 @@ namespace DynamORM.Tests.Modify DestroyTestDatabase(); } - [Test] - public void TestTypedUpdateSetAndWhere() - { - var cmd = Database.Update() - .SetTyped(u => u.Code, "777") - .WhereTyped(u => u.Id == 1 && u.Code == "1"); - - Assert.AreEqual( - string.Format("UPDATE \"sample_users\" SET \"code\" = [${0}] WHERE (\"id\" = [${1}]) AND (\"code\" = [${2}])", - cmd.Parameters.Keys.ElementAt(0), - cmd.Parameters.Keys.ElementAt(1), - cmd.Parameters.Keys.ElementAt(2)), - cmd.CommandText()); - } - [Test] public void TestTypedUpdateBuilderSetAndWhere() { @@ -62,17 +47,6 @@ namespace DynamORM.Tests.Modify cmd.CommandText()); } - [Test] - public void TestTypedDeleteWhere() - { - var cmd = Database.Delete() - .WhereTyped(u => u.Id == 2); - - Assert.AreEqual( - string.Format("DELETE FROM \"sample_users\" WHERE (\"id\" = [${0}])", cmd.Parameters.Keys.First()), - cmd.CommandText()); - } - [Test] public void TestTypedDeleteBuilderWhere() { @@ -84,20 +58,6 @@ namespace DynamORM.Tests.Modify cmd.CommandText()); } - [Test] - public void TestTypedInsertColumns() - { - var cmd = Database.Insert() - .InsertTyped(u => u.Code, "900") - .InsertTyped(u => u.First, "Typed"); - - Assert.AreEqual( - string.Format("INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES ([${0}], [${1}])", - cmd.Parameters.Keys.ElementAt(0), - cmd.Parameters.Keys.ElementAt(1)), - cmd.CommandText()); - } - [Test] public void TestTypedInsertBuilderColumns() { diff --git a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs index 65412de..eddbd13 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs @@ -20,7 +20,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedDeleteQueryBuilder Where(Expression> predicate) { - this.WhereTyped(predicate); + TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } diff --git a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs index 8b072bf..257f7ee 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs @@ -20,7 +20,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value) { - this.InsertTyped(selector, value); + base.Insert(TypedModifyHelper.GetMappedColumn(selector), value); return this; } diff --git a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs index a2227f6..efa2c26 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs @@ -20,13 +20,13 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedUpdateQueryBuilder Where(Expression> predicate) { - this.WhereTyped(predicate); + TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } public IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value) { - this.SetTyped(selector, value); + base.Values(TypedModifyHelper.GetMappedColumn(selector), value); return this; } diff --git a/DynamORM/Builders/Implementation/TypedModifyHelper.cs b/DynamORM/Builders/Implementation/TypedModifyHelper.cs new file mode 100644 index 0000000..8625026 --- /dev/null +++ b/DynamORM/Builders/Implementation/TypedModifyHelper.cs @@ -0,0 +1,114 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq; +using System.Linq.Expressions; +using DynamORM.Mapper; + +namespace DynamORM.Builders.Implementation +{ + /// Helper methods for typed modify builders. + internal static class TypedModifyHelper + { + public static string GetMappedColumn(Expression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + return GetMappedColumn(typeof(T), selector.Body); + } + + public static void ApplyWhere(Action addCondition, Expression> predicate) + { + if (addCondition == null) + throw new ArgumentNullException("addCondition"); + if (predicate == null) + throw new ArgumentNullException("predicate"); + + ApplyWhereInternal(typeof(T), addCondition, predicate.Body); + } + + private static void ApplyWhereInternal(Type modelType, Action addCondition, Expression expression) + { + expression = UnwrapConvert(expression); + BinaryExpression be = expression as BinaryExpression; + if (be == null) + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + + if (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And) + { + ApplyWhereInternal(modelType, addCondition, be.Left); + ApplyWhereInternal(modelType, addCondition, be.Right); + return; + } + + string col = GetMappedColumn(modelType, be.Left); + object val = EvaluateExpression(be.Right); + + switch (be.NodeType) + { + case ExpressionType.Equal: + addCondition(col, DynamicColumn.CompareOperator.Eq, val); + return; + case ExpressionType.NotEqual: + addCondition(col, DynamicColumn.CompareOperator.Not, val); + return; + case ExpressionType.GreaterThan: + addCondition(col, DynamicColumn.CompareOperator.Gt, val); + return; + case ExpressionType.GreaterThanOrEqual: + addCondition(col, DynamicColumn.CompareOperator.Gte, val); + return; + case ExpressionType.LessThan: + addCondition(col, DynamicColumn.CompareOperator.Lt, val); + return; + case ExpressionType.LessThanOrEqual: + addCondition(col, DynamicColumn.CompareOperator.Lte, val); + return; + } + + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + } + + private static string GetMappedColumn(Type modelType, Expression expression) + { + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member == null) + throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); + + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + return mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + } + + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression && + (((UnaryExpression)expression).NodeType == ExpressionType.Convert || + ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) + expression = ((UnaryExpression)expression).Operand; + + return expression; + } + + private static object EvaluateExpression(Expression expression) + { + expression = UnwrapConvert(expression); + var objectMember = Expression.Convert(expression, typeof(object)); + var getter = Expression.Lambda>(objectMember).Compile(); + return getter(); + } + } +} diff --git a/DynamORM/Builders/TypedModifyExtensions.cs b/DynamORM/Builders/TypedModifyExtensions.cs deleted file mode 100644 index f1687c9..0000000 --- a/DynamORM/Builders/TypedModifyExtensions.cs +++ /dev/null @@ -1,155 +0,0 @@ -/* - * DynamORM - Dynamic Object-Relational Mapping library. - * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) - * All rights reserved. - */ - -using System; -using System.Linq; -using System.Linq.Expressions; -using DynamORM.Mapper; - -namespace DynamORM.Builders -{ - /// Typed helper extensions for update/insert/delete fluent APIs. - public static class TypedModifyExtensions - { - /// Add typed where predicate (AND-composed comparisons) for update builder. - public static IDynamicUpdateQueryBuilder WhereTyped(this IDynamicUpdateQueryBuilder builder, Expression> predicate) - { - AddTypedWhere(builder, predicate == null ? null : predicate.Body); - return builder; - } - - /// Add typed where predicate (AND-composed comparisons) for delete builder. - public static IDynamicDeleteQueryBuilder WhereTyped(this IDynamicDeleteQueryBuilder builder, Expression> predicate) - { - AddTypedWhere(builder, predicate == null ? null : predicate.Body); - return builder; - } - - /// Add typed value assignment for update builder. - public static IDynamicUpdateQueryBuilder SetTyped(this IDynamicUpdateQueryBuilder builder, Expression> selector, object value) - { - string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); - return builder.Values(col, value); - } - - /// Add typed value assignment for insert builder. - public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, Expression> selector, object value) - { - string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); - return builder.Insert(col, value); - } - - /// Insert mapped object with compile-time type information. - public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, T value) where T : class - { - return builder.Insert(value); - } - - /// Update mapped object with compile-time type information. - public static IDynamicUpdateQueryBuilder UpdateTyped(this IDynamicUpdateQueryBuilder builder, T value) where T : class - { - return builder.Update(value); - } - - private static void AddTypedWhere(dynamic builder, Expression expression) - { - if (expression == null) - throw new ArgumentNullException("predicate"); - - expression = UnwrapConvert(expression); - BinaryExpression be = expression as BinaryExpression; - - if (be != null && (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And)) - { - AddTypedWhere(builder, be.Left); - AddTypedWhere(builder, be.Right); - return; - } - - if (be != null) - { - string col = GetColumnName(GetRootParameterType(be.Left), be.Left); - object val = EvaluateExpression(be.Right); - - switch (be.NodeType) - { - case ExpressionType.Equal: - builder.Where(col, DynamicColumn.CompareOperator.Eq, val); - return; - case ExpressionType.NotEqual: - builder.Where(col, DynamicColumn.CompareOperator.Not, val); - return; - case ExpressionType.GreaterThan: - builder.Where(col, DynamicColumn.CompareOperator.Gt, val); - return; - case ExpressionType.GreaterThanOrEqual: - builder.Where(col, DynamicColumn.CompareOperator.Gte, val); - return; - case ExpressionType.LessThan: - builder.Where(col, DynamicColumn.CompareOperator.Lt, val); - return; - case ExpressionType.LessThanOrEqual: - builder.Where(col, DynamicColumn.CompareOperator.Lte, val); - return; - } - } - - throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); - } - - private static Type GetRootParameterType(Expression expression) - { - expression = UnwrapConvert(expression); - MemberExpression m = expression as MemberExpression; - if (m != null && m.Expression is ParameterExpression) - return ((ParameterExpression)m.Expression).Type; - - throw new NotSupportedException(string.Format("Unsupported typed selector: {0}", expression)); - } - - private static string GetColumnName(Type modelType, Expression expression) - { - if (modelType == null) - modelType = GetRootParameterType(expression); - - expression = UnwrapConvert(expression); - MemberExpression member = expression as MemberExpression; - if (member == null) - throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); - - DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); - if (mapper == null) - throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); - - string col = mapper.PropertyMap.TryGetValue(member.Member.Name) - ?? mapper.PropertyMap - .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Value) - .FirstOrDefault() - ?? member.Member.Name; - - return col; - } - - private static Expression UnwrapConvert(Expression expression) - { - while (expression is UnaryExpression && - (((UnaryExpression)expression).NodeType == ExpressionType.Convert || - ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) - expression = ((UnaryExpression)expression).Operand; - - return expression; - } - - private static object EvaluateExpression(Expression expression) - { - expression = UnwrapConvert(expression); - var objectMember = Expression.Convert(expression, typeof(object)); - var getter = Expression.Lambda>(objectMember).Compile(); - return getter(); - } - } -}