diff --git a/Specification.EntityFrameworkCore/src/Ardalis.Specification.EntityFrameworkCore/Evaluators/AsTrackingEvaluator.cs b/Specification.EntityFrameworkCore/src/Ardalis.Specification.EntityFrameworkCore/Evaluators/AsTrackingEvaluator.cs new file mode 100644 index 00000000..f67273b2 --- /dev/null +++ b/Specification.EntityFrameworkCore/src/Ardalis.Specification.EntityFrameworkCore/Evaluators/AsTrackingEvaluator.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.EntityFrameworkCore; + +namespace Ardalis.Specification.EntityFrameworkCore +{ + public class AsTrackingEvaluator : IEvaluator + { + private AsTrackingEvaluator() { } + public static AsTrackingEvaluator Instance { get; } = new AsTrackingEvaluator(); + + public bool IsCriteriaEvaluator { get; } = true; + + public IQueryable GetQuery(IQueryable query, ISpecification specification) where T : class + { + if (specification.AsTracking) + { + query = query.AsTracking(); + } + + return query; + } + } +} diff --git a/Specification.EntityFrameworkCore/src/Ardalis.Specification.EntityFrameworkCore/Evaluators/SpecificationEvaluator.cs b/Specification.EntityFrameworkCore/src/Ardalis.Specification.EntityFrameworkCore/Evaluators/SpecificationEvaluator.cs index aa388b90..559e5786 100644 --- a/Specification.EntityFrameworkCore/src/Ardalis.Specification.EntityFrameworkCore/Evaluators/SpecificationEvaluator.cs +++ b/Specification.EntityFrameworkCore/src/Ardalis.Specification.EntityFrameworkCore/Evaluators/SpecificationEvaluator.cs @@ -30,11 +30,10 @@ public SpecificationEvaluator(bool cacheEnabled = false) OrderEvaluator.Instance, PaginationEvaluator.Instance, AsNoTrackingEvaluator.Instance, + AsNoTrackingWithIdentityResolutionEvaluator.Instance, + AsTrackingEvaluator.Instance, IgnoreQueryFiltersEvaluator.Instance, -#if !NETSTANDARD2_0 - AsSplitQueryEvaluator.Instance, - AsNoTrackingWithIdentityResolutionEvaluator.Instance -#endif + AsSplitQueryEvaluator.Instance }); } diff --git a/Specification/src/Ardalis.Specification/Builder/SpecificationBuilderExtensions.cs b/Specification/src/Ardalis.Specification/Builder/SpecificationBuilderExtensions.cs index 0e44907a..b3da0032 100644 --- a/Specification/src/Ardalis.Specification/Builder/SpecificationBuilderExtensions.cs +++ b/Specification/src/Ardalis.Specification/Builder/SpecificationBuilderExtensions.cs @@ -370,6 +370,35 @@ public static ICacheSpecificationBuilder EnableCache( return cacheBuilder; } + /// + /// If the entity instances are modified, this will be detected + /// by the change tracker. + /// + /// + public static ISpecificationBuilder AsTracking( + this ISpecificationBuilder specificationBuilder) where T : class + => AsTracking(specificationBuilder, true); + + /// + /// If the entity instances are modified, this will be detected + /// by the change tracker. + /// + /// + /// If false, the setting will be discarded. + public static ISpecificationBuilder AsTracking( + this ISpecificationBuilder specificationBuilder, + bool condition) where T : class + { + if (condition) + { + specificationBuilder.Specification.AsNoTracking = false; + specificationBuilder.Specification.AsNoTrackingWithIdentityResolution = false; + specificationBuilder.Specification.AsTracking = true; + } + + return specificationBuilder; + } + /// /// If the entity instances are modified, this will not be detected /// by the change tracker. @@ -391,6 +420,8 @@ public static ISpecificationBuilder AsNoTracking( { if (condition) { + specificationBuilder.Specification.AsTracking = false; + specificationBuilder.Specification.AsNoTrackingWithIdentityResolution = false; specificationBuilder.Specification.AsNoTracking = true; } @@ -463,6 +494,8 @@ public static ISpecificationBuilder AsNoTrackingWithIdentityResolution( { if (condition) { + specificationBuilder.Specification.AsTracking = false; + specificationBuilder.Specification.AsNoTracking = false; specificationBuilder.Specification.AsNoTrackingWithIdentityResolution = true; } diff --git a/Specification/src/Ardalis.Specification/ISpecification.cs b/Specification/src/Ardalis.Specification/ISpecification.cs index bfbae635..d0ed1609 100644 --- a/Specification/src/Ardalis.Specification/ISpecification.cs +++ b/Specification/src/Ardalis.Specification/ISpecification.cs @@ -97,6 +97,12 @@ public interface ISpecification /// string? CacheKey { get; } + /// + /// Returns whether or not the change tracker will track any of the entities + /// that are returned. + /// + bool AsTracking { get; } + /// /// Returns whether or not the change tracker will track any of the entities /// that are returned. When true, if the entity instances are modified, this will not be detected diff --git a/Specification/src/Ardalis.Specification/Specification.cs b/Specification/src/Ardalis.Specification/Specification.cs index 9e3bb4df..02019ae9 100644 --- a/Specification/src/Ardalis.Specification/Specification.cs +++ b/Specification/src/Ardalis.Specification/Specification.cs @@ -108,6 +108,9 @@ public virtual bool IsSatisfiedBy(T entity) /// public bool CacheEnabled { get; internal set; } + /// + public bool AsTracking { get; internal set; } = false; + /// public bool AsNoTracking { get; internal set; } = false; diff --git a/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsNoTracking.cs b/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsNoTracking.cs index ec4d7beb..dc0ec496 100644 --- a/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsNoTracking.cs +++ b/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsNoTracking.cs @@ -29,5 +29,13 @@ public void FlagsAsNoTracking_GivenSpecWithAsNoTracking() spec.AsNoTracking.Should().Be(true); } + + [Fact] + public void FlagsAsNoTracking_GivenSpecWithAsTrackingAndEndWithAsNoTracking() + { + var spec = new CompanyByIdWithAsTrackingAsUntrackedSpec(1); + + spec.AsNoTracking.Should().Be(true); + } } } diff --git a/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsNoTrackingWithIdentityResolution.cs b/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsNoTrackingWithIdentityResolution.cs index 4916bc76..3fb6ff80 100644 --- a/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsNoTrackingWithIdentityResolution.cs +++ b/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsNoTrackingWithIdentityResolution.cs @@ -29,5 +29,13 @@ public void FlagsAsNoTracking_GivenSpecWithAsNoTrackingWithIdentityResolution() spec.AsNoTrackingWithIdentityResolution.Should().Be(true); } + + [Fact] + public void FlagsAsNoTracking_GivenSpecWithAsTrackingAndEndWithAsNoTrackingWithIdentityResolution() + { + var spec = new CompanyByIdWithAsTrackingAsUntrackedWithIdentityResolutionSpec(1); + + spec.AsNoTrackingWithIdentityResolution.Should().Be(true); + } } } diff --git a/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsTracking.cs b/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsTracking.cs new file mode 100644 index 00000000..9785bac0 --- /dev/null +++ b/Specification/tests/Ardalis.Specification.UnitTests/BuilderTests/SpecificationBuilderExtensions_AsTracking.cs @@ -0,0 +1,41 @@ +using Ardalis.Specification.UnitTests.Fixture.Specs; +using FluentAssertions; +using Xunit; + +namespace Ardalis.Specification.UnitTests.BuilderTests +{ + public class SpecificationBuilderExtensions_AsTracking + { + [Fact] + public void DoesNothing_GivenSpecWithoutAsTracking() + { + var spec = new StoreEmptySpec(); + + spec.AsTracking.Should().Be(false); + } + + [Fact] + public void DoesNothing_GivenAsTrackingWithFalseCondition() + { + var spec = new CompanyByIdWithFalseConditions(1); + + spec.AsTracking.Should().Be(false); + } + + [Fact] + public void FlagsAsTracking_GivenSpecWithAsTracking() + { + var spec = new CompanyByIdAsTrackedSpec(1); + + spec.AsTracking.Should().Be(true); + } + + [Fact] + public void FlagsAsTracking_GivenSpecWithAsNoTrackingAndEndWithAsTracking() + { + var spec = new CompanyByIdWithAsNoTrackingAsTrackedSpec(1); + + spec.AsTracking.Should().Be(true); + } + } +} diff --git a/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdAsTrackedSpec.cs b/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdAsTrackedSpec.cs new file mode 100644 index 00000000..88efb470 --- /dev/null +++ b/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdAsTrackedSpec.cs @@ -0,0 +1,12 @@ +using Ardalis.Specification.UnitTests.Fixture.Entities; + +namespace Ardalis.Specification.UnitTests.Fixture.Specs +{ + public class CompanyByIdAsTrackedSpec : Specification, ISingleResultSpecification + { + public CompanyByIdAsTrackedSpec(int id) + { + Query.Where(company => company.Id == id).AsTracking(); + } + } +} diff --git a/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdWithAsNoTrackingAsTrackedSpec.cs b/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdWithAsNoTrackingAsTrackedSpec.cs new file mode 100644 index 00000000..342e3f59 --- /dev/null +++ b/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdWithAsNoTrackingAsTrackedSpec.cs @@ -0,0 +1,12 @@ +using Ardalis.Specification.UnitTests.Fixture.Entities; + +namespace Ardalis.Specification.UnitTests.Fixture.Specs +{ + public class CompanyByIdWithAsNoTrackingAsTrackedSpec : Specification, ISingleResultSpecification + { + public CompanyByIdWithAsNoTrackingAsTrackedSpec(int id) + { + Query.Where(company => company.Id == id).AsNoTracking().AsNoTrackingWithIdentityResolution().AsTracking(); + } + } +} diff --git a/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdWithAsTrackingAsUntrackedSpec.cs b/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdWithAsTrackingAsUntrackedSpec.cs new file mode 100644 index 00000000..16201d6d --- /dev/null +++ b/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdWithAsTrackingAsUntrackedSpec.cs @@ -0,0 +1,12 @@ +using Ardalis.Specification.UnitTests.Fixture.Entities; + +namespace Ardalis.Specification.UnitTests.Fixture.Specs +{ + public class CompanyByIdWithAsTrackingAsUntrackedSpec : Specification, ISingleResultSpecification + { + public CompanyByIdWithAsTrackingAsUntrackedSpec(int id) + { + Query.Where(company => company.Id == id).AsTracking().AsNoTracking(); + } + } +} diff --git a/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdWithAsTrackingAsUntrackedWithIdentityResolutionSpec.cs b/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdWithAsTrackingAsUntrackedWithIdentityResolutionSpec.cs new file mode 100644 index 00000000..f4fd1197 --- /dev/null +++ b/Specification/tests/Ardalis.Specification.UnitTests/Fixture/Specs/CompanyByIdWithAsTrackingAsUntrackedWithIdentityResolutionSpec.cs @@ -0,0 +1,13 @@ +using Ardalis.Specification.UnitTests.Fixture.Entities; + +namespace Ardalis.Specification.UnitTests.Fixture.Specs +{ + public class CompanyByIdWithAsTrackingAsUntrackedWithIdentityResolutionSpec : Specification, + ISingleResultSpecification + { + public CompanyByIdWithAsTrackingAsUntrackedWithIdentityResolutionSpec(int id) + { + Query.Where(company => company.Id == id).AsTracking().AsNoTrackingWithIdentityResolution(); + } + } +}