Browse Source

Merge pull request #24913 from ugurozturk/fixTrackingIfDBDefaultBehaviorNoTracking

Improve FindAsync method to handle entity attachment and state manage…
pull/24922/head
Ma Liming 1 month ago
committed by GitHub
parent
commit
bd8afddd61
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EfCoreRepositoryExtensions.cs
  2. 4
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
  3. 91
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ChangeTracking/ChangeTrackingInterceptor_Tests.cs

2
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EfCoreRepositoryExtensions.cs

@ -49,6 +49,6 @@ public static class EfCoreRepositoryExtensions
public static IQueryable<TEntity> AsNoTrackingIf<TEntity>(this IQueryable<TEntity> queryable, bool condition)
where TEntity : class, IEntity
{
return condition ? queryable.AsNoTracking() : queryable;
return condition ? queryable.AsNoTracking() : queryable.AsTracking();
}
}

4
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs

@ -478,9 +478,7 @@ public class EfCoreRepository<TDbContext, TEntity, TKey> : EfCoreRepository<TDbC
{
return includeDetails
? await (await WithDetailsAsync()).OrderBy(e => e.Id).FirstOrDefaultAsync(e => e.Id!.Equals(id), GetCancellationToken(cancellationToken))
: !ShouldTrackingEntityChange()
? await (await GetQueryableAsync()).OrderBy(e => e.Id).FirstOrDefaultAsync(e => e.Id!.Equals(id), GetCancellationToken(cancellationToken))
: await (await GetDbSetAsync()).FindAsync(new object[] { id! }, GetCancellationToken(cancellationToken));
: await (await GetQueryableAsync()).OrderBy(e => e.Id).FirstOrDefaultAsync(e => e.Id!.Equals(id), GetCancellationToken(cancellationToken));
}
public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)

91
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ChangeTracking/ChangeTrackingInterceptor_Tests.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.ChangeTracking;
@ -83,6 +84,96 @@ public class ChangeTrackingInterceptor_Tests : TestAppTestBase<AbpEntityFramewor
});
}
[Fact]
public async Task Repository_Should_Override_Global_NoTracking_When_EntityChangeTracking_Is_Enabled()
{
await AddSomePeopleAsync();
var repository = GetRequiredService<IRepository<Person, Guid>>();
Guid personId = default;
await WithUnitOfWorkAsync(async () =>
{
var p = await repository.FindAsync(x => x.Name == "people1");
p.ShouldNotBeNull();
personId = p.Id;
});
// Simulate global NoTracking configured on DbContext (e.g. optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking))
await WithUnitOfWorkAsync(async () =>
{
var db = await repository.GetDbContextAsync();
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
db.ChangeTracker.Entries().Count().ShouldBe(0);
// FindAsync(id): ShouldTrackingEntityChange()=true, GetQueryableAsync() uses AsTracking() to override global NoTracking
var person = await repository.FindAsync(personId, includeDetails: false);
person.ShouldNotBeNull();
db.ChangeTracker.Entries<Person>().Count().ShouldBe(1);
db.Entry(person).State.ShouldBe(EntityState.Unchanged);
});
await WithUnitOfWorkAsync(async () =>
{
var db = await repository.GetDbContextAsync();
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
db.ChangeTracker.Entries().Count().ShouldBe(0);
// FindAsync(predicate): same - AsTracking() overrides global NoTracking
var person = await repository.FindAsync(x => x.Name == "people1");
person.ShouldNotBeNull();
db.ChangeTracker.Entries<Person>().Count().ShouldBe(1);
db.Entry(person).State.ShouldBe(EntityState.Unchanged);
});
await WithUnitOfWorkAsync(async () =>
{
var db = await repository.GetDbContextAsync();
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
db.ChangeTracker.Entries().Count().ShouldBe(0);
// GetListAsync: same - AsTracking() overrides global NoTracking
var list = await repository.GetListAsync();
list.Count.ShouldBeGreaterThan(0);
db.ChangeTracker.Entries<Person>().Count().ShouldBe(list.Count);
});
}
[Fact]
public async Task Repository_Should_Respect_NoTracking_When_EntityChangeTracking_Is_Disabled_With_Global_NoTracking()
{
await AddSomePeopleAsync();
var repository = GetRequiredService<IRepository<Person, Guid>>();
Guid personId = default;
await WithUnitOfWorkAsync(async () =>
{
var p = await repository.FindAsync(x => x.Name == "people1");
p.ShouldNotBeNull();
personId = p.Id;
});
await WithUnitOfWorkAsync(async () =>
{
var db = await repository.GetDbContextAsync();
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
db.ChangeTracker.Entries().Count().ShouldBe(0);
// When tracking is explicitly disabled, entity should NOT be tracked regardless of global setting
using (repository.DisableTracking())
{
var person = await repository.FindAsync(personId, includeDetails: false);
person.ShouldNotBeNull();
db.ChangeTracker.Entries().Count().ShouldBe(0);
var list = await repository.GetListAsync();
list.Count.ShouldBeGreaterThan(0);
db.ChangeTracker.Entries().Count().ShouldBe(0);
}
});
}
private async Task AddSomePeopleAsync()
{
var repository = GetRequiredService<IRepository<Person, Guid>>();

Loading…
Cancel
Save