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

Commit cf5e76a

Browse files
Shane32sdanyliv
andauthored
Fix EnableChangeTracker and AsNoTracking support (#57)
* Add failing tests * Fix EnableChangeTracker property * Fixed AsNoTracking issue. Co-authored-by: Svyatoslav Danyliv <[email protected]>
1 parent c891079 commit cf5e76a

File tree

4 files changed

+75
-10
lines changed

4 files changed

+75
-10
lines changed

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ public static ILinqToDBForEFTools Implementation
9393
{
9494
_implementation = value ?? throw new ArgumentNullException(nameof(value));
9595
_metadataReaders.Clear();
96-
_defaultMeadataReader = new Lazy<IMetadataReader>(() => Implementation.CreateMetadataReader(null, null, null));
96+
_defaultMetadataReader = new Lazy<IMetadataReader>(() => Implementation.CreateMetadataReader(null, null, null));
9797
}
9898
}
9999

100100
static readonly ConcurrentDictionary<IModel, IMetadataReader> _metadataReaders = new ConcurrentDictionary<IModel, IMetadataReader>();
101101

102-
static Lazy<IMetadataReader> _defaultMeadataReader;
102+
static Lazy<IMetadataReader> _defaultMetadataReader;
103103

104104
/// <summary>
105105
/// Clears internal caches
@@ -129,7 +129,7 @@ public static IMetadataReader GetMetadataReader([JetBrains.Annotations.CanBeNull
129129
RelationalSqlTranslatingExpressionVisitorDependencies dependencies, IRelationalTypeMappingSource mappingSource)
130130
{
131131
if (model == null)
132-
return _defaultMeadataReader.Value;
132+
return _defaultMetadataReader.Value;
133133

134134
return _metadataReaders.GetOrAdd(model, m => Implementation.CreateMetadataReader(model, dependencies, mappingSource));
135135
}
@@ -517,7 +517,7 @@ public static DbContext GetCurrentContext(IQueryable query)
517517
public static bool EnableChangeTracker
518518
{
519519
get => Implementation.EnableChangeTracker;
520-
set => Implementation.EnableChangeTracker = true;
520+
set => Implementation.EnableChangeTracker = value;
521521
}
522522

523523
}

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsDataConnection.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ public class LinqToDBForEFToolsDataConnection : DataConnection, IExpressionPrepr
2222
readonly IModel _model;
2323
readonly Func<Expression, IDataContext, DbContext, IModel, Expression> _transformFunc;
2424

25-
private IEntityType _lastEntityType;
26-
private Type _lastType;
25+
private IEntityType _lastEntityType;
26+
private Type _lastType;
2727
private IStateManager _stateManager;
2828

29-
public bool Tracking { get; set; }
29+
public bool Tracking { get; set; }
3030

31-
public DbContext Context { get; }
31+
public DbContext Context { get; }
3232

3333
public LinqToDBForEFToolsDataConnection(
3434
[CanBeNull] DbContext context,

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsImplDefault.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,9 @@ static readonly MethodInfo
534534
static readonly MethodInfo L2DBFromSqlMethodInfo =
535535
MemberHelper.MethodOfGeneric<IDataContext>(dc => dc.FromSql<object>(new Common.RawSqlString()));
536536

537+
static readonly MethodInfo L2DBRemoveOrderByMethodInfo =
538+
MemberHelper.MethodOfGeneric<IQueryable<object>>(q => q.RemoveOrderBy());
539+
537540
static readonly ConstructorInfo RawSqlStringConstructor = MemberHelper.ConstructorOf(() => new Common.RawSqlString(""));
538541

539542
static readonly ConstructorInfo DataParameterConstructor = MemberHelper.ConstructorOf(() => new DataParameter("", "", DataType.Undefined, ""));
@@ -700,7 +703,8 @@ static List<Expression> CompactTree(List<Expression> items, ExpressionType nodeT
700703
public virtual Expression TransformExpression(Expression expression, IDataContext dc, DbContext ctx, IModel model)
701704
{
702705
var ignoreQueryFilters = false;
703-
var tracking = true;
706+
var tracking = true;
707+
var ignoreTracking = false;
704708

705709
Expression LocalTransform(Expression e)
706710
{
@@ -795,6 +799,12 @@ Expression LocalTransform(Expression e)
795799
methodCall.Arguments.Select(a => a.Transform(l => LocalTransform(l)))
796800
.ToArray());
797801
}
802+
else if (generic == L2DBRemoveOrderByMethodInfo)
803+
{
804+
// This is workaround. EagerLoading runs query again with RemoveOrderBy method.
805+
// it is only one possible way now how to detect nested query.
806+
ignoreTracking = true;
807+
}
798808

799809
if (isTunnel)
800810
return methodCall.Arguments[0].Transform(l => LocalTransform(l));
@@ -848,7 +858,7 @@ Expression LocalTransform(Expression e)
848858
newExpression, Expression.NewArrayInit(typeof(Type)));
849859
}
850860

851-
if (dc is LinqToDBForEFToolsDataConnection dataConnection)
861+
if (!ignoreTracking && dc is LinqToDBForEFToolsDataConnection dataConnection)
852862
{
853863
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
854864
dataConnection.Tracking = tracking;

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,61 @@ public async Task TestChangeTracker([Values(true, false)] bool enableFilter)
568568
}
569569
}
570570

571+
[Test]
572+
public async Task TestChangeTrackerDisabled1([Values(true, false)] bool enableFilter)
573+
{
574+
using (var ctx = CreateContext(enableFilter))
575+
{
576+
var query = ctx.Orders
577+
.Include(o => o.OrderDetails)
578+
.ThenInclude(d => d.Product)
579+
.ThenInclude(p => p.OrderDetails)
580+
.AsNoTracking();
581+
582+
// var efResult = await query.ToArrayAsync();
583+
var result = await query.ToLinqToDB().ToArrayAsync();
584+
585+
var orderDetail = result[0].OrderDetails.First();
586+
orderDetail.UnitPrice = orderDetail.UnitPrice * 1.1m;
587+
588+
ctx.ChangeTracker.DetectChanges();
589+
var changedEntry = ctx.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).SingleOrDefault();
590+
Assert.AreEqual(changedEntry, null);
591+
ctx.SaveChanges();
592+
}
593+
}
594+
595+
[Test]
596+
public async Task TestChangeTrackerDisabled2([Values(true, false)] bool enableFilter)
597+
{
598+
LinqToDBForEFTools.EnableChangeTracker = false;
599+
try
600+
{
601+
using (var ctx = CreateContext(enableFilter))
602+
{
603+
var query = ctx.Orders
604+
.Include(o => o.OrderDetails)
605+
.ThenInclude(d => d.Product)
606+
.ThenInclude(p => p.OrderDetails);
607+
608+
// var efResult = await query.ToArrayAsync();
609+
var result = await query.ToLinqToDB().ToArrayAsync();
610+
611+
var orderDetail = result[0].OrderDetails.First();
612+
orderDetail.UnitPrice = orderDetail.UnitPrice * 1.1m;
613+
614+
ctx.ChangeTracker.DetectChanges();
615+
var changedEntry = ctx.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).SingleOrDefault();
616+
Assert.AreEqual(changedEntry, null);
617+
ctx.SaveChanges();
618+
}
619+
}
620+
finally
621+
{
622+
LinqToDBForEFTools.EnableChangeTracker = true;
623+
}
624+
}
625+
571626
[Test]
572627
public async Task TestSetUpdate([Values(true, false)] bool enableFilter)
573628
{

0 commit comments

Comments
 (0)