Skip to content
This repository was archived by the owner on Feb 1, 2025. It is now read-only.

Commit 755d142

Browse files
authored
Merge pull request #312 from linq2db/version6
Release 6.13.0
2 parents 87b83dd + 51a8955 commit 755d142

File tree

8 files changed

+171
-30
lines changed

8 files changed

+171
-30
lines changed

Build/linq2db.Default.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>6.12.0</Version>
3+
<Version>6.13.0</Version>
44

55
<Authors>Svyatoslav Danyliv, Igor Tkachev, Dmitry Lukashenko, Ilya Chudin</Authors>
66
<Product>Linq to DB</Product>

Directory.Packages.props

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<PackageVersion Include="NUnit" Version="3.13.3" />
66
<PackageVersion Include="FluentAssertions" Version="6.10.0" />
77

8-
<PackageVersion Include="linq2db" Version="5.0.0" />
9-
<PackageVersion Include="linq2db.Tools" Version="5.0.0" />
8+
<PackageVersion Include="linq2db" Version="5.1.0" />
9+
<PackageVersion Include="linq2db.Tools" Version="5.1.0" />
1010

1111
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
1212

NuGet.config

+7-12
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
<?xml version="1.0" encoding="utf-8"?>
2-
<configuration>
3-
<packageSources>
4-
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
5-
<add key="linq2db" value="https://pkgs.dev.azure.com/linq2db/linq2db/_packaging/linq2db/nuget/v3/index.json" />
6-
</packageSources>
7-
<packageSourceMapping>
8-
<packageSource key="NuGet">
9-
<package pattern="*" />
10-
</packageSource>
11-
</packageSourceMapping>
12-
</configuration>
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<packageSources>
4+
<clear />
5+
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
6+
</packageSources>
7+
</configuration>

NuGet/linq2db.EntityFrameworkCore.nuspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<dependencies>
1717
<group targetFramework="net6.0">
1818
<dependency id="Microsoft.EntityFrameworkCore.Relational" version="6.0.5" />
19-
<dependency id="linq2db" version="5.0.0" />
19+
<dependency id="linq2db" version="5.1.0" />
2020
</group>
2121
</dependencies>
2222
</metadata>

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsImplDefault.cs

+124-12
Original file line numberDiff line numberDiff line change
@@ -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 QueryRootExpression 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.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+
9851097
static Expression EnsureEnumerable(Expression expression, MappingSchema mappingSchema)
9861098
{
9871099
var enumerable = typeof(IEnumerable<>).MakeGenericType(GetEnumerableElementType(expression.Type, mappingSchema));

Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/Northwind.Mapping/ProductsMap.cs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ public class ProductsMap : BaseEntityMap<Product>
88
{
99
public override void Configure(EntityTypeBuilder<Product> builder)
1010
{
11+
builder.ToTable(t => t.IsTemporal());
12+
1113
builder.HasKey(e => e.ProductId);
1214

1315
builder.HasIndex(e => e.CategoryId)

Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ToolsTests.cs

+32
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using System.Threading.Tasks;
44
using FluentAssertions;
5+
using FluentAssertions.Specialized;
56
using LinqToDB.Data;
67
using LinqToDB.EntityFrameworkCore.BaseTests;
78
using LinqToDB.EntityFrameworkCore.BaseTests.Models.Northwind;
@@ -835,5 +836,36 @@ public void TestTagWith([Values(true, false)] bool enableFilter)
835836
}
836837
}
837838

839+
840+
[Test]
841+
public void TestTemporalTables([Values(true, false)] bool enableFilter)
842+
{
843+
using (var ctx = CreateContext(enableFilter))
844+
{
845+
var query1 = ctx.Products.TemporalAsOf(DateTime.UtcNow);
846+
var query2 = ctx.Products.TemporalFromTo(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow);
847+
var query3 = ctx.Products.TemporalAll();
848+
var query4 = ctx.Products.TemporalBetween(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow);
849+
var query5 = ctx.Products.TemporalContainedIn(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow);
850+
851+
var result1 = query1.ToLinqToDB().ToArray();
852+
var result2 = query2.ToLinqToDB().ToArray();
853+
var result3 = query3.ToLinqToDB().ToArray();
854+
var result4 = query4.ToLinqToDB().ToArray();
855+
var result5 = query5.ToLinqToDB().ToArray();
856+
857+
var allQuery =
858+
from p in ctx.Products.ToLinqToDB()
859+
from q1 in ctx.Products.TemporalAsOf(DateTime.UtcNow).Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
860+
from q2 in ctx.Products.TemporalFromTo(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow).Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
861+
from q3 in ctx.Products.TemporalAll().Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
862+
from q4 in ctx.Products.TemporalBetween(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow).Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
863+
from q5 in ctx.Products.TemporalContainedIn(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow).Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
864+
select p;
865+
866+
var result = allQuery.ToArray();
867+
}
868+
}
869+
838870
}
839871
}

azure-pipelines.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
variables:
22
solution: 'linq2db.EFCore.sln'
33
build_configuration: 'Release'
4-
assemblyVersion: 6.12.0
5-
nugetVersion: 6.12.0
4+
assemblyVersion: 6.13.0
5+
nugetVersion: 6.13.0
66
artifact_nugets: 'nugets'
77

88
# build on commits to important branches (master + release branches):

0 commit comments

Comments
 (0)