Skip to content

Commit

Permalink
Merge pull request #312 from linq2db/version6
Browse files Browse the repository at this point in the history
Release 6.13.0
  • Loading branch information
sdanyliv authored Mar 21, 2023
2 parents 87b83dd + 51a8955 commit 755d142
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Build/linq2db.Default.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>6.12.0</Version>
<Version>6.13.0</Version>

<Authors>Svyatoslav Danyliv, Igor Tkachev, Dmitry Lukashenko, Ilya Chudin</Authors>
<Product>Linq to DB</Product>
Expand Down
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="FluentAssertions" Version="6.10.0" />

<PackageVersion Include="linq2db" Version="5.0.0" />
<PackageVersion Include="linq2db.Tools" Version="5.0.0" />
<PackageVersion Include="linq2db" Version="5.1.0" />
<PackageVersion Include="linq2db.Tools" Version="5.1.0" />

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

Expand Down
19 changes: 7 additions & 12 deletions NuGet.config
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
<add key="linq2db" value="https://pkgs.dev.azure.com/linq2db/linq2db/_packaging/linq2db/nuget/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="NuGet">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
2 changes: 1 addition & 1 deletion NuGet/linq2db.EntityFrameworkCore.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<dependencies>
<group targetFramework="net6.0">
<dependency id="Microsoft.EntityFrameworkCore.Relational" version="6.0.5" />
<dependency id="linq2db" version="5.0.0" />
<dependency id="linq2db" version="5.1.0" />
</group>
</dependencies>
</metadata>
Expand Down
136 changes: 124 additions & 12 deletions Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsImplDefault.cs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,13 @@ public virtual MappingSchema GetMappingSchema(

static readonly MethodInfo ToSql = MemberHelper.MethodOfGeneric(() => Sql.ToSql(1));

private static readonly MethodInfo AsSqlServerTable = MemberHelper.MethodOfGeneric<ITable<object>>(q => DataProvider.SqlServer.SqlServerTools.AsSqlServer(q));
private static readonly MethodInfo TemporalAsOfTable = MemberHelper.MethodOfGeneric<ISqlServerSpecificTable<object>>(t => SqlServerHints.TemporalTableAsOf(t, default));
private static readonly MethodInfo TemporalFromTo = MemberHelper.MethodOfGeneric<ISqlServerSpecificTable<object>>(t => SqlServerHints.TemporalTableFromTo(t, default, default));
private static readonly MethodInfo TemporalBetween = MemberHelper.MethodOfGeneric<ISqlServerSpecificTable<object>>(t => SqlServerHints.TemporalTableBetween(t, default, default));
private static readonly MethodInfo TemporalContainedIn = MemberHelper.MethodOfGeneric<ISqlServerSpecificTable<object>>(t => SqlServerHints.TemporalTableContainedIn(t, default, default));
private static readonly MethodInfo TemporalAll = MemberHelper.MethodOfGeneric<ISqlServerSpecificTable<object>>(t => SqlServerHints.TemporalTableAll(t));

/// <summary>
/// Removes conversions from expression.
/// </summary>
Expand Down Expand Up @@ -726,6 +733,27 @@ static List<Expression> CompactTree(List<Expression> items, ExpressionType nodeT
return result;
}

/// <summary>
/// Gets current property value via reflection.
/// </summary>
/// <typeparam name="TValue">Property value type.</typeparam>
/// <param name="obj">Object instance</param>
/// <param name="propName">Property name</param>
/// <returns>Property value.</returns>
/// <exception cref="InvalidOperationException"></exception>
protected static TValue GetPropValue<TValue>(object obj, string propName)
{
var prop = obj.GetType().GetProperty(propName);
if (prop == null)
{
throw new InvalidOperationException($"Property {obj.GetType().Name}.{propName} not found.");
}
var propValue = prop.GetValue(obj);
if (propValue == default)
return default!;
return (TValue)propValue;
}

/// <summary>
/// Transforms EF Core expression tree to LINQ To DB expression.
/// Method replaces EF Core <see cref="EntityQueryable{TResult}"/> instances with LINQ To DB
Expand Down Expand Up @@ -948,19 +976,12 @@ TransformInfo LocalTransform(Expression e)

case ExpressionType.Extension:
{
if (dc != null && e is FromSqlQueryRootExpression fromSqlQueryRoot)
{
//convert the arguments from the FromSqlOnQueryable method from EF, to a L2DB FromSql call
return new TransformInfo(Expression.Call(null,
L2DBFromSqlMethodInfo.MakeGenericMethod(fromSqlQueryRoot.EntityType.ClrType),
Expression.Constant(dc),
Expression.New(RawSqlStringConstructor, Expression.Constant(fromSqlQueryRoot.Sql)),
fromSqlQueryRoot.Argument));
}
else if (dc != null && e is QueryRootExpression queryRoot)
if (dc != null)
{
var newExpr = Expression.Call(null, Methods.LinqToDB.GetTable.MakeGenericMethod(queryRoot.EntityType.ClrType), Expression.Constant(dc));
return new TransformInfo(newExpr);
if (e is QueryRootExpression queryRoot)
{
return new TransformInfo(TransformQueryRootExpression(dc, queryRoot));
}
}

break;
Expand All @@ -982,6 +1003,97 @@ TransformInfo LocalTransform(Expression e)
return newExpression;
}

/// <summary>
/// Transforms <see cref="QueryRootExpression"/> descendants to linq2db analogue. Handles Temporal tables also.
/// </summary>
/// <param name="dc">Data context.</param>
/// <param name="queryRoot">Query root expression</param>
/// <returns>Transformed expression.</returns>
protected virtual Expression TransformQueryRootExpression(IDataContext dc, QueryRootExpression queryRoot)
{
static Expression GetAsOfSqlServer(Expression getTableExpr, Type entityType)
{
return Expression.Call(
AsSqlServerTable.MakeGenericMethod(entityType),
getTableExpr);
}

if (queryRoot is FromSqlQueryRootExpression fromSqlQueryRoot)
{
//convert the arguments from the FromSqlOnQueryable method from EF, to a L2DB FromSql call
return Expression.Call(null,
L2DBFromSqlMethodInfo.MakeGenericMethod(fromSqlQueryRoot.EntityType.ClrType),
Expression.Constant(dc),
Expression.New(RawSqlStringConstructor, Expression.Constant(fromSqlQueryRoot.Sql)),
fromSqlQueryRoot.Argument);
}

var entityType = queryRoot.EntityType.ClrType;
var getTableExpr = Expression.Call(null, Methods.LinqToDB.GetTable.MakeGenericMethod(entityType),
Expression.Constant(dc));

var expressionTypeName = queryRoot.GetType().Name;
if (expressionTypeName == "TemporalAsOfQueryRootExpression")
{
var pointInTime = GetPropValue<DateTime>(queryRoot, "PointInTime");

var asOf = Expression.Call(TemporalAsOfTable.MakeGenericMethod(entityType),
GetAsOfSqlServer(getTableExpr, entityType),
Expression.Constant(pointInTime));

return asOf;
}

if (expressionTypeName == "TemporalFromToQueryRootExpression")
{
var from = GetPropValue<DateTime>(queryRoot, "From");
var to = GetPropValue<DateTime>(queryRoot, "To");

var fromTo = Expression.Call(TemporalFromTo.MakeGenericMethod(entityType),
GetAsOfSqlServer(getTableExpr, entityType),
Expression.Constant(from),
Expression.Constant(to));

return fromTo;
}

if (expressionTypeName == "TemporalBetweenQueryRootExpression")
{
var from = GetPropValue<DateTime>(queryRoot, "From");
var to = GetPropValue<DateTime>(queryRoot, "To");

var fromTo = Expression.Call(TemporalBetween.MakeGenericMethod(entityType),
GetAsOfSqlServer(getTableExpr, entityType),
Expression.Constant(from),
Expression.Constant(to));

return fromTo;
}

if (expressionTypeName == "TemporalContainedInQueryRootExpression")
{
var from = GetPropValue<DateTime>(queryRoot, "From");
var to = GetPropValue<DateTime>(queryRoot, "To");

var fromTo = Expression.Call(TemporalContainedIn.MakeGenericMethod(entityType),
GetAsOfSqlServer(getTableExpr, entityType),
Expression.Constant(from),
Expression.Constant(to));

return fromTo;
}

if (expressionTypeName == "TemporalAllQueryRootExpression")
{
var all = Expression.Call(TemporalAll.MakeGenericMethod(entityType),
GetAsOfSqlServer(getTableExpr, entityType));

return all;
}

return getTableExpr;
}

static Expression EnsureEnumerable(Expression expression, MappingSchema mappingSchema)
{
var enumerable = typeof(IEnumerable<>).MakeGenericType(GetEnumerableElementType(expression.Type, mappingSchema));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public class ProductsMap : BaseEntityMap<Product>
{
public override void Configure(EntityTypeBuilder<Product> builder)
{
builder.ToTable(t => t.IsTemporal());

builder.HasKey(e => e.ProductId);

builder.HasIndex(e => e.CategoryId)
Expand Down
32 changes: 32 additions & 0 deletions Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ToolsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Specialized;
using LinqToDB.Data;
using LinqToDB.EntityFrameworkCore.BaseTests;
using LinqToDB.EntityFrameworkCore.BaseTests.Models.Northwind;
Expand Down Expand Up @@ -835,5 +836,36 @@ public void TestTagWith([Values(true, false)] bool enableFilter)
}
}


[Test]
public void TestTemporalTables([Values(true, false)] bool enableFilter)
{
using (var ctx = CreateContext(enableFilter))
{
var query1 = ctx.Products.TemporalAsOf(DateTime.UtcNow);
var query2 = ctx.Products.TemporalFromTo(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow);
var query3 = ctx.Products.TemporalAll();
var query4 = ctx.Products.TemporalBetween(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow);
var query5 = ctx.Products.TemporalContainedIn(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow);

var result1 = query1.ToLinqToDB().ToArray();
var result2 = query2.ToLinqToDB().ToArray();
var result3 = query3.ToLinqToDB().ToArray();
var result4 = query4.ToLinqToDB().ToArray();
var result5 = query5.ToLinqToDB().ToArray();

var allQuery =
from p in ctx.Products.ToLinqToDB()
from q1 in ctx.Products.TemporalAsOf(DateTime.UtcNow).Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
from q2 in ctx.Products.TemporalFromTo(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow).Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
from q3 in ctx.Products.TemporalAll().Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
from q4 in ctx.Products.TemporalBetween(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow).Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
from q5 in ctx.Products.TemporalContainedIn(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow).Where(q => q.ProductId == p.ProductId).DefaultIfEmpty()
select p;

var result = allQuery.ToArray();
}
}

}
}
4 changes: 2 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
variables:
solution: 'linq2db.EFCore.sln'
build_configuration: 'Release'
assemblyVersion: 6.12.0
nugetVersion: 6.12.0
assemblyVersion: 6.13.0
nugetVersion: 6.13.0
artifact_nugets: 'nugets'

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

0 comments on commit 755d142

Please sign in to comment.