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

Commit b9d22b2

Browse files
committed
Updated to linq2db 3.4.3
Backported changes fro 5.x
1 parent 2145e5f commit b9d22b2

File tree

11 files changed

+147
-33
lines changed

11 files changed

+147
-33
lines changed

Build/linq2db.Default.props

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
<GenerateAssemblyVersionAttribute>true</GenerateAssemblyVersionAttribute>
2828
<GenerateAssemblyFileVersionAttribute>true</GenerateAssemblyFileVersionAttribute>
2929
<GenerateNeutralResourcesLanguageAttribute>false</GenerateNeutralResourcesLanguageAttribute>
30-
30+
3131
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
3232
</PropertyGroup>
3333

34-
<PropertyGroup>
35-
<AnalysisLevel>preview</AnalysisLevel>
36-
<EnableNETAnalyzers>true</EnableNETAnalyzers>
37-
<RunAnalyzers>true</RunAnalyzers>
38-
</PropertyGroup>
34+
<PropertyGroup>
35+
<AnalysisLevel>preview</AnalysisLevel>
36+
<EnableNETAnalyzers>true</EnableNETAnalyzers>
37+
<RunAnalyzers>true</RunAnalyzers>
38+
</PropertyGroup>
3939
</Project>

Directory.Packages.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<PackageVersion Include="NUnit" Version="3.13.2" />
66
<PackageVersion Include="FluentAssertions" Version="5.10.3" />
77

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

1111
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
1212
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" />

NuGet/linq2db.EntityFrameworkCore.nuspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<dependencies>
1717
<group targetFramework=".NETStandard2.0">
1818
<dependency id="Microsoft.EntityFrameworkCore.Relational" version="3.1.11" />
19-
<dependency id="linq2db" version="3.4.2" />
19+
<dependency id="linq2db" version="3.4.3" />
2020
</group>
2121
</dependencies>
2222
</metadata>

Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ public T[] GetAttributes<T>(Type type, bool inherit = true) where T : Attribute
8181
return e;
8282
});
8383

84+
filterBody = LinqToDBForEFTools.TransformExpression(filterBody, null, null, _model);
85+
8486
// we have found dependency, check for compatibility
8587

8688
var filterLambda = Expression.Lambda(filterBody, filter.Parameters[0]);

Source/LinqToDB.EntityFrameworkCore/ILinqToDBForEFTools.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public interface ILinqToDBForEFTools
7777
/// <param name="ctx">Optional DbContext instance.</param>
7878
/// <param name="model">EF Core data model instance.</param>
7979
/// <returns>Transformed expression.</returns>
80-
Expression TransformExpression(Expression expression, IDataContext dc, DbContext? ctx, IModel? model);
80+
Expression TransformExpression(Expression expression, IDataContext? dc, DbContext? ctx, IModel? model);
8181

8282
/// <summary>
8383
/// Extracts <see cref="DbContext"/> instance from <see cref="IQueryable"/> object.

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ public static MappingSchema GetMappingSchema(
231231
/// <param name="ctx">Optional DbContext instance.</param>
232232
/// <param name="model">EF Core data model instance.</param>
233233
/// <returns>Transformed expression.</returns>
234-
public static Expression TransformExpression(Expression expression, IDataContext dc, DbContext? ctx, IModel? model)
234+
public static Expression TransformExpression(Expression expression, IDataContext? dc, DbContext? ctx, IModel? model)
235235
{
236236
return Implementation.TransformExpression(expression, dc, ctx, model);
237237
}

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsDataConnection.cs

Lines changed: 107 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@
22
using System.Data;
33
using System.Linq;
44
using System.Linq.Expressions;
5-
6-
using Microsoft.EntityFrameworkCore.Metadata;
5+
using System.Reflection;
76

87
using Microsoft.EntityFrameworkCore;
8+
using Microsoft.EntityFrameworkCore.Metadata;
99
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
1010
using Microsoft.EntityFrameworkCore.Infrastructure;
1111
using Microsoft.EntityFrameworkCore.Storage;
12+
using Microsoft.Extensions.Caching.Memory;
13+
using Microsoft.Extensions.Options;
1214

1315
namespace LinqToDB.EntityFrameworkCore
1416
{
1517
using System.Diagnostics.CodeAnalysis;
1618
using Data;
1719
using DataProvider;
1820
using Linq;
21+
using Expressions;
1922

2023
/// <summary>
2124
/// linq2db EF.Core data connection.
@@ -29,6 +32,11 @@ public class LinqToDBForEFToolsDataConnection : DataConnection, IExpressionPrepr
2932
private Type? _lastType;
3033
private IStateManager? _stateManager;
3134

35+
private static IMemoryCache _entityKeyGetterCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
36+
37+
private static MethodInfo TryGetEntryMethodInfo =
38+
MemberHelper.MethodOf<IStateManager>(sm => sm.TryGetEntry(null!, Array.Empty<object>()));
39+
3240
/// <summary>
3341
/// Change tracker enable flag.
3442
/// </summary>
@@ -121,8 +129,61 @@ public Expression ProcessExpression(Expression expression)
121129
return _transformFunc(expression, this, Context, _model);
122130
}
123131

132+
private class TypeKey
133+
{
134+
public TypeKey(IEntityType entityType, IModel? model)
135+
{
136+
EntityType = entityType;
137+
Model = model;
138+
}
139+
140+
public IEntityType EntityType { get; }
141+
public IModel? Model { get; }
142+
143+
protected bool Equals(TypeKey other)
144+
{
145+
return EntityType.Equals(other.EntityType) && Equals(Model, other.Model);
146+
}
147+
148+
public override bool Equals(object? obj)
149+
{
150+
if (ReferenceEquals(null, obj))
151+
{
152+
return false;
153+
}
154+
155+
if (ReferenceEquals(this, obj))
156+
{
157+
return true;
158+
}
159+
160+
if (obj.GetType() != this.GetType())
161+
{
162+
return false;
163+
}
164+
165+
return Equals((TypeKey)obj);
166+
}
167+
168+
public override int GetHashCode()
169+
{
170+
unchecked
171+
{
172+
return (EntityType.GetHashCode() * 397) ^ (Model != null ? Model.GetHashCode() : 0);
173+
}
174+
}
175+
}
176+
124177
private void OnEntityCreatedHandler(EntityCreatedEventArgs args)
125178
{
179+
// Do not allow to store in ChangeTracker temporary tables
180+
if ((args.TableOptions & TableOptions.IsTemporaryOptionSet) != 0)
181+
return;
182+
183+
// Do not allow to store in ChangeTracker tables from different server
184+
if (args.ServerName != null)
185+
return;
186+
126187
if (!LinqToDBForEFTools.EnableChangeTracker
127188
|| !Tracking
128189
|| Context!.ChangeTracker.QueryTrackingBehavior == QueryTrackingBehavior.NoTracking)
@@ -138,6 +199,10 @@ private void OnEntityCreatedHandler(EntityCreatedEventArgs args)
138199
if (_lastEntityType == null)
139200
return;
140201

202+
// Do not allow to store in ChangeTracker tables that has different name
203+
if (args.TableName != _lastEntityType.GetTableName())
204+
return;
205+
141206
if (_stateManager == null)
142207
_stateManager = Context.GetService<IStateManager>();
143208

@@ -146,22 +211,18 @@ private void OnEntityCreatedHandler(EntityCreatedEventArgs args)
146211
//
147212
InternalEntityEntry? entry = null;
148213

149-
foreach (var key in _lastEntityType.GetKeys())
214+
var kacheKey = new TypeKey (_lastEntityType, _model);
215+
216+
var retrievalFunc = _entityKeyGetterCache.GetOrCreate(kacheKey, ce =>
150217
{
151-
//TODO: Find faster way
152-
var keyArray = key.Properties.Where(p => p.PropertyInfo != null || p.FieldInfo != null).Select(p =>
153-
p.PropertyInfo != null
154-
? p.PropertyInfo.GetValue(args.Entity)
155-
: p.FieldInfo.GetValue(args.Entity)).ToArray();
218+
ce.SlidingExpiration = TimeSpan.FromHours(1);
219+
return CreateEntityRetrievalFunc(((TypeKey)ce.Key).EntityType);
220+
});
156221

157-
if (keyArray.Length == key.Properties.Count)
158-
{
159-
entry = _stateManager.TryGetEntry(key, keyArray);
222+
if (retrievalFunc == null)
223+
return;
160224

161-
if (entry != null)
162-
break;
163-
}
164-
}
225+
entry = retrievalFunc(_stateManager, args.Entity);
165226

166227
if (entry == null)
167228
{
@@ -171,6 +232,37 @@ private void OnEntityCreatedHandler(EntityCreatedEventArgs args)
171232
args.Entity = entry.Entity;
172233
}
173234

235+
private Func<IStateManager, object, InternalEntityEntry?>? CreateEntityRetrievalFunc(IEntityType entityType)
236+
{
237+
var stateManagerParam = Expression.Parameter(typeof(IStateManager), "sm");
238+
var objParam = Expression.Parameter(typeof(object), "o");
239+
240+
var variable = Expression.Variable(entityType.ClrType, "e");
241+
var assignExpr = Expression.Assign(variable, Expression.Convert(objParam, entityType.ClrType));
242+
243+
var key = entityType.GetKeys().FirstOrDefault();
244+
245+
var arrayExpr = key.Properties.Where(p => p.PropertyInfo != null || p.FieldInfo != null).Select(p =>
246+
Expression.Convert(Expression.MakeMemberAccess(variable, p.PropertyInfo ?? (MemberInfo)p.FieldInfo),
247+
typeof(object)))
248+
.ToArray();
249+
250+
if (arrayExpr.Length == 0)
251+
return null;
252+
253+
var newArrayExpression = Expression.NewArrayInit(typeof(object), arrayExpr);
254+
var body =
255+
Expression.Block(new[] { variable },
256+
assignExpr,
257+
Expression.Call(stateManagerParam, TryGetEntryMethodInfo, Expression.Constant(key),
258+
newArrayExpression));
259+
260+
var lambda =
261+
Expression.Lambda<Func<IStateManager, object, InternalEntityEntry?>>(body, stateManagerParam, objParam);
262+
263+
return lambda.Compile();
264+
}
265+
174266
private void CopyDatabaseProperties()
175267
{
176268
var commandTimeout = Context?.Database.GetCommandTimeout();

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsImplDefault.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ static List<Expression> CompactTree(List<Expression> items, ExpressionType nodeT
767767
/// <param name="ctx">Optional DbContext instance.</param>
768768
/// <param name="model">EF Core data model instance.</param>
769769
/// <returns>Transformed expression.</returns>
770-
public virtual Expression TransformExpression(Expression expression, IDataContext dc, DbContext? ctx, IModel? model)
770+
public virtual Expression TransformExpression(Expression expression, IDataContext? dc, DbContext? ctx, IModel? model)
771771
{
772772
var tracking = true;
773773
var ignoreTracking = false;
@@ -780,7 +780,7 @@ TransformInfo LocalTransform(Expression e)
780780
{
781781
case ExpressionType.Constant:
782782
{
783-
if (typeof(EntityQueryable<>).IsSameOrParentOf(e.Type) || typeof(DbSet<>).IsSameOrParentOf(e.Type))
783+
if (dc != null && typeof(EntityQueryable<>).IsSameOrParentOf(e.Type) || typeof(DbSet<>).IsSameOrParentOf(e.Type))
784784
{
785785
var entityType = e.Type.GenericTypeArguments[0];
786786
var newExpr = Expression.Call(null, Methods.LinqToDB.GetTable.MakeGenericMethod(entityType), Expression.Constant(dc));

Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
<ItemGroup>
66
<PackageReference Include="FluentAssertions" />
7+
</ItemGroup>
8+
9+
<ItemGroup>
710
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
811
</ItemGroup>
912

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,23 @@ public async Task TestChangeTrackerDisabled2([Values(true, false)] bool enableFi
592592
}
593593
}
594594

595+
[Test]
596+
public async Task TestChangeTrackerTemporaryTable([Values(true, false)] bool enableFilter)
597+
{
598+
using var ctx = CreateContext(enableFilter);
599+
600+
var query = ctx.Orders;
601+
602+
using var db = ctx.CreateLinqToDbConnection();
603+
604+
using var temp = await db.CreateTempTableAsync(query, tableName: "#Orders");
605+
606+
var result = temp.Take(2).ToList();
607+
608+
ctx.Orders.Local.Should().BeEmpty();
609+
}
610+
611+
595612
[Test]
596613
public void NavigationProperties()
597614
{

0 commit comments

Comments
 (0)