@@ -560,6 +560,13 @@ public virtual MappingSchema GetMappingSchema(
560560
561561 static readonly MethodInfo ToSql = MemberHelper . MethodOfGeneric ( ( ) => Sql . ToSql ( 1 ) ) ;
562562
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+
563570 /// <summary>
564571 /// Removes conversions from expression.
565572 /// </summary>
@@ -726,6 +733,27 @@ static List<Expression> CompactTree(List<Expression> items, ExpressionType nodeT
726733 return result ;
727734 }
728735
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+
729757 /// <summary>
730758 /// Transforms EF Core expression tree to LINQ To DB expression.
731759 /// Method replaces EF Core <see cref="EntityQueryable{TResult}"/> instances with LINQ To DB
@@ -948,19 +976,12 @@ TransformInfo LocalTransform(Expression e)
948976
949977 case ExpressionType . Extension :
950978 {
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 EntityQueryRootExpression queryRoot )
979+ if ( dc != null )
961980 {
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+ }
964985 }
965986
966987 break ;
@@ -982,6 +1003,97 @@ TransformInfo LocalTransform(Expression e)
9821003 return newExpression ;
9831004 }
9841005
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 . ElementType ;
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+
9851097 static Expression EnsureEnumerable ( Expression expression , MappingSchema mappingSchema )
9861098 {
9871099 var enumerable = typeof ( IEnumerable < > ) . MakeGenericType ( GetEnumerableElementType ( expression . Type , mappingSchema ) ) ;
0 commit comments