@@ -560,6 +560,13 @@ public virtual MappingSchema GetMappingSchema(
560
560
561
561
static readonly MethodInfo ToSql = MemberHelper . MethodOfGeneric ( ( ) => Sql . ToSql ( 1 ) ) ;
562
562
563
+ private static readonly MethodInfo AsSqlServerTable = MemberHelper . MethodOfGeneric < ITable < object > > ( q => DataProvider . SqlServer . SqlServerTools . AsSqlServer ( q ) ) ;
564
+ private static readonly MethodInfo TemporalAsOfTable = MemberHelper . MethodOfGeneric < ISqlServerSpecificTable < object > > ( t => SqlServerHints . TemporalTableAsOf ( t , default ) ) ;
565
+ private static readonly MethodInfo TemporalFromTo = MemberHelper . MethodOfGeneric < ISqlServerSpecificTable < object > > ( t => SqlServerHints . TemporalTableFromTo ( t , default , default ) ) ;
566
+ private static readonly MethodInfo TemporalBetween = MemberHelper . MethodOfGeneric < ISqlServerSpecificTable < object > > ( t => SqlServerHints . TemporalTableBetween ( t , default , default ) ) ;
567
+ private static readonly MethodInfo TemporalContainedIn = MemberHelper . MethodOfGeneric < ISqlServerSpecificTable < object > > ( t => SqlServerHints . TemporalTableContainedIn ( t , default , default ) ) ;
568
+ private static readonly MethodInfo TemporalAll = MemberHelper . MethodOfGeneric < ISqlServerSpecificTable < object > > ( t => SqlServerHints . TemporalTableAll ( t ) ) ;
569
+
563
570
/// <summary>
564
571
/// Removes conversions from expression.
565
572
/// </summary>
@@ -726,6 +733,27 @@ static List<Expression> CompactTree(List<Expression> items, ExpressionType nodeT
726
733
return result ;
727
734
}
728
735
736
+ /// <summary>
737
+ /// Gets current property value via reflection.
738
+ /// </summary>
739
+ /// <typeparam name="TValue">Property value type.</typeparam>
740
+ /// <param name="obj">Object instance</param>
741
+ /// <param name="propName">Property name</param>
742
+ /// <returns>Property value.</returns>
743
+ /// <exception cref="InvalidOperationException"></exception>
744
+ protected static TValue GetPropValue < TValue > ( object obj , string propName )
745
+ {
746
+ var prop = obj . GetType ( ) . GetProperty ( propName ) ;
747
+ if ( prop == null )
748
+ {
749
+ throw new InvalidOperationException ( $ "Property { obj . GetType ( ) . Name } .{ propName } not found.") ;
750
+ }
751
+ var propValue = prop . GetValue ( obj ) ;
752
+ if ( propValue == default )
753
+ return default ! ;
754
+ return ( TValue ) propValue ;
755
+ }
756
+
729
757
/// <summary>
730
758
/// Transforms EF Core expression tree to LINQ To DB expression.
731
759
/// Method replaces EF Core <see cref="EntityQueryable{TResult}"/> instances with LINQ To DB
@@ -948,19 +976,12 @@ TransformInfo LocalTransform(Expression e)
948
976
949
977
case ExpressionType . Extension :
950
978
{
951
- if ( dc != null && e is FromSqlQueryRootExpression fromSqlQueryRoot )
952
- {
953
- //convert the arguments from the FromSqlOnQueryable method from EF, to a L2DB FromSql call
954
- return new TransformInfo ( Expression . Call ( null ,
955
- L2DBFromSqlMethodInfo . MakeGenericMethod ( fromSqlQueryRoot . EntityType . ClrType ) ,
956
- Expression . Constant ( dc ) ,
957
- Expression . New ( RawSqlStringConstructor , Expression . Constant ( fromSqlQueryRoot . Sql ) ) ,
958
- fromSqlQueryRoot . Argument ) ) ;
959
- }
960
- else if ( dc != null && e is QueryRootExpression queryRoot )
979
+ if ( dc != null )
961
980
{
962
- var newExpr = Expression . Call ( null , Methods . LinqToDB . GetTable . MakeGenericMethod ( queryRoot . EntityType . ClrType ) , Expression . Constant ( dc ) ) ;
963
- return new TransformInfo ( newExpr ) ;
981
+ if ( e is QueryRootExpression queryRoot )
982
+ {
983
+ return new TransformInfo ( TransformQueryRootExpression ( dc , queryRoot ) ) ;
984
+ }
964
985
}
965
986
966
987
break ;
@@ -982,6 +1003,97 @@ TransformInfo LocalTransform(Expression e)
982
1003
return newExpression ;
983
1004
}
984
1005
1006
+ /// <summary>
1007
+ /// Transforms <see cref="QueryRootExpression"/> descendants to linq2db analogue. Handles Temporal tables also.
1008
+ /// </summary>
1009
+ /// <param name="dc">Data context.</param>
1010
+ /// <param name="queryRoot">Query root expression</param>
1011
+ /// <returns>Transformed expression.</returns>
1012
+ protected virtual Expression TransformQueryRootExpression ( IDataContext dc , QueryRootExpression queryRoot )
1013
+ {
1014
+ static Expression GetAsOfSqlServer ( Expression getTableExpr , Type entityType )
1015
+ {
1016
+ return Expression . Call (
1017
+ AsSqlServerTable . MakeGenericMethod ( entityType ) ,
1018
+ getTableExpr ) ;
1019
+ }
1020
+
1021
+ if ( queryRoot is FromSqlQueryRootExpression fromSqlQueryRoot )
1022
+ {
1023
+ //convert the arguments from the FromSqlOnQueryable method from EF, to a L2DB FromSql call
1024
+ return Expression . Call ( null ,
1025
+ L2DBFromSqlMethodInfo . MakeGenericMethod ( fromSqlQueryRoot . EntityType . ClrType ) ,
1026
+ Expression . Constant ( dc ) ,
1027
+ Expression . New ( RawSqlStringConstructor , Expression . Constant ( fromSqlQueryRoot . Sql ) ) ,
1028
+ fromSqlQueryRoot . Argument ) ;
1029
+ }
1030
+
1031
+ var entityType = queryRoot . EntityType . ClrType ;
1032
+ var getTableExpr = Expression . Call ( null , Methods . LinqToDB . GetTable . MakeGenericMethod ( entityType ) ,
1033
+ Expression . Constant ( dc ) ) ;
1034
+
1035
+ var expressionTypeName = queryRoot . GetType ( ) . Name ;
1036
+ if ( expressionTypeName == "TemporalAsOfQueryRootExpression" )
1037
+ {
1038
+ var pointInTime = GetPropValue < DateTime > ( queryRoot , "PointInTime" ) ;
1039
+
1040
+ var asOf = Expression . Call ( TemporalAsOfTable . MakeGenericMethod ( entityType ) ,
1041
+ GetAsOfSqlServer ( getTableExpr , entityType ) ,
1042
+ Expression . Constant ( pointInTime ) ) ;
1043
+
1044
+ return asOf ;
1045
+ }
1046
+
1047
+ if ( expressionTypeName == "TemporalFromToQueryRootExpression" )
1048
+ {
1049
+ var from = GetPropValue < DateTime > ( queryRoot , "From" ) ;
1050
+ var to = GetPropValue < DateTime > ( queryRoot , "To" ) ;
1051
+
1052
+ var fromTo = Expression . Call ( TemporalFromTo . MakeGenericMethod ( entityType ) ,
1053
+ GetAsOfSqlServer ( getTableExpr , entityType ) ,
1054
+ Expression . Constant ( from ) ,
1055
+ Expression . Constant ( to ) ) ;
1056
+
1057
+ return fromTo ;
1058
+ }
1059
+
1060
+ if ( expressionTypeName == "TemporalBetweenQueryRootExpression" )
1061
+ {
1062
+ var from = GetPropValue < DateTime > ( queryRoot , "From" ) ;
1063
+ var to = GetPropValue < DateTime > ( queryRoot , "To" ) ;
1064
+
1065
+ var fromTo = Expression . Call ( TemporalBetween . MakeGenericMethod ( entityType ) ,
1066
+ GetAsOfSqlServer ( getTableExpr , entityType ) ,
1067
+ Expression . Constant ( from ) ,
1068
+ Expression . Constant ( to ) ) ;
1069
+
1070
+ return fromTo ;
1071
+ }
1072
+
1073
+ if ( expressionTypeName == "TemporalContainedInQueryRootExpression" )
1074
+ {
1075
+ var from = GetPropValue < DateTime > ( queryRoot , "From" ) ;
1076
+ var to = GetPropValue < DateTime > ( queryRoot , "To" ) ;
1077
+
1078
+ var fromTo = Expression . Call ( TemporalContainedIn . MakeGenericMethod ( entityType ) ,
1079
+ GetAsOfSqlServer ( getTableExpr , entityType ) ,
1080
+ Expression . Constant ( from ) ,
1081
+ Expression . Constant ( to ) ) ;
1082
+
1083
+ return fromTo ;
1084
+ }
1085
+
1086
+ if ( expressionTypeName == "TemporalAllQueryRootExpression" )
1087
+ {
1088
+ var all = Expression . Call ( TemporalAll . MakeGenericMethod ( entityType ) ,
1089
+ GetAsOfSqlServer ( getTableExpr , entityType ) ) ;
1090
+
1091
+ return all ;
1092
+ }
1093
+
1094
+ return getTableExpr ;
1095
+ }
1096
+
985
1097
static Expression EnsureEnumerable ( Expression expression , MappingSchema mappingSchema )
986
1098
{
987
1099
var enumerable = typeof ( IEnumerable < > ) . MakeGenericType ( GetEnumerableElementType ( expression . Type , mappingSchema ) ) ;
0 commit comments