Add typed modify and join fluent extensions
This commit is contained in:
86
DynamORM/Builders/TypedJoinExtensions.cs
Normal file
86
DynamORM/Builders/TypedJoinExtensions.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.Helpers;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Typed join helpers for typed select builder.</summary>
|
||||
public static class TypedJoinExtensions
|
||||
{
|
||||
/// <summary>Add typed join on mapped members. Supports simple equality join expression only.</summary>
|
||||
public static IDynamicTypedSelectQueryBuilder<TLeft> JoinTyped<TLeft, TRight>(
|
||||
this IDynamicTypedSelectQueryBuilder<TLeft> builder,
|
||||
string alias,
|
||||
Expression<Func<TLeft, TRight, bool>> on,
|
||||
string joinType = "INNER JOIN")
|
||||
{
|
||||
if (builder == null) throw new ArgumentNullException("builder");
|
||||
if (on == null) throw new ArgumentNullException("on");
|
||||
|
||||
var rightMapper = DynamicMapperCache.GetMapper(typeof(TRight));
|
||||
if (rightMapper == null)
|
||||
throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName));
|
||||
|
||||
string rightTable = string.IsNullOrEmpty(rightMapper.Table.NullOr(t => t.Name))
|
||||
? typeof(TRight).Name
|
||||
: rightMapper.Table.Name;
|
||||
|
||||
string rightOwner = rightMapper.Table.NullOr(t => t.Owner);
|
||||
string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (builder.Tables.Count + 1).ToString() : alias;
|
||||
|
||||
var be = on.Body as BinaryExpression;
|
||||
if (be == null || be.NodeType != ExpressionType.Equal)
|
||||
throw new NotSupportedException("JoinTyped currently supports only equality join expressions.");
|
||||
|
||||
string leftPrefix = builder.Tables.FirstOrDefault().NullOr(t => string.IsNullOrEmpty(t.Alias) ? t.Name : t.Alias, null);
|
||||
if (string.IsNullOrEmpty(leftPrefix))
|
||||
throw new InvalidOperationException("JoinTyped requires source table to be present.");
|
||||
|
||||
string leftExpr = ResolveMappedSide(be.Left, typeof(TLeft), leftPrefix, builder.Database);
|
||||
string rightExpr = ResolveMappedSide(be.Right, typeof(TRight), rightAlias, builder.Database);
|
||||
|
||||
string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : builder.Database.DecorateName(rightOwner) + ".";
|
||||
string rightTableExpr = ownerPrefix + builder.Database.DecorateName(rightTable);
|
||||
string joinExpr = string.Format("{0} {1} AS {2} ON ({3} = {4})", joinType, rightTableExpr, rightAlias, leftExpr, rightExpr);
|
||||
|
||||
builder.Join(x => joinExpr);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static string ResolveMappedSide(Expression expression, Type modelType, string prefix, DynamicDatabase db)
|
||||
{
|
||||
expression = UnwrapConvert(expression);
|
||||
var member = expression as MemberExpression;
|
||||
if (member == null)
|
||||
throw new NotSupportedException("Join side must be mapped member access.");
|
||||
|
||||
var mapper = DynamicMapperCache.GetMapper(modelType);
|
||||
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 string.Format("{0}.{1}", prefix, db.DecorateName(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
155
DynamORM/Builders/TypedModifyExtensions.cs
Normal file
155
DynamORM/Builders/TypedModifyExtensions.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
/// <summary>Typed helper extensions for update/insert/delete fluent APIs.</summary>
|
||||
public static class TypedModifyExtensions
|
||||
{
|
||||
/// <summary>Add typed where predicate (AND-composed comparisons) for update builder.</summary>
|
||||
public static IDynamicUpdateQueryBuilder WhereTyped<T>(this IDynamicUpdateQueryBuilder builder, Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
AddTypedWhere(builder, predicate == null ? null : predicate.Body);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>Add typed where predicate (AND-composed comparisons) for delete builder.</summary>
|
||||
public static IDynamicDeleteQueryBuilder WhereTyped<T>(this IDynamicDeleteQueryBuilder builder, Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
AddTypedWhere(builder, predicate == null ? null : predicate.Body);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>Add typed value assignment for update builder.</summary>
|
||||
public static IDynamicUpdateQueryBuilder SetTyped<T, TValue>(this IDynamicUpdateQueryBuilder builder, Expression<Func<T, TValue>> selector, object value)
|
||||
{
|
||||
string col = GetColumnName(typeof(T), selector == null ? null : selector.Body);
|
||||
return builder.Values(col, value);
|
||||
}
|
||||
|
||||
/// <summary>Add typed value assignment for insert builder.</summary>
|
||||
public static IDynamicInsertQueryBuilder InsertTyped<T, TValue>(this IDynamicInsertQueryBuilder builder, Expression<Func<T, TValue>> selector, object value)
|
||||
{
|
||||
string col = GetColumnName(typeof(T), selector == null ? null : selector.Body);
|
||||
return builder.Insert(col, value);
|
||||
}
|
||||
|
||||
/// <summary>Insert mapped object with compile-time type information.</summary>
|
||||
public static IDynamicInsertQueryBuilder InsertTyped<T>(this IDynamicInsertQueryBuilder builder, T value) where T : class
|
||||
{
|
||||
return builder.Insert(value);
|
||||
}
|
||||
|
||||
/// <summary>Update mapped object with compile-time type information.</summary>
|
||||
public static IDynamicUpdateQueryBuilder UpdateTyped<T>(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<Func<object>>(objectMember).Compile();
|
||||
return getter();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user