Support IQueryable<T>.AsTracking<T> methods
codelovercc opened this issue · comments
I've noticed that ISpecificationBuilder<T>
has AsNoTracking<T>
method.
Situation one:
In my abstract repository class, it has a method:
protected virtual IQueryable<TEntity> ApplySpecification(ISpecification<TEntity> specification
bool evaluateCriteriaOnly = false)
{
return SpecificationEvaluator.GetQuery(EntitySet.AsNoTracking(), specification, evaluateCriteriaOnly);
}
EntitySet
is an instance of DbSet<TEntity>
. EntitySet.AsNoTracking()
makes that the query results are not tracked by default.
Now I want some Specification to make sure its query results are being tracked. How do I do in my Specification class?
Since the ISpecificationBuilder<T>.AsNoTracking<T>
is supported, I think we should support ISpecificationBuilder<T>.AsTracking<T>
just like IQueryable<T>.AsTracking<T>
do.
Situation two:
Let the specifications control tracking.
I have a pagination specification class:
public abstract class PaginationSpecification<T> : Specification<T>, ISearchSpecification<T> where T : class
{
public sealed override ISpecificationBuilder<T> Query => null!;
protected PaginationSpecification(IPagination pagination)
: this(pagination, InMemorySpecificationEvaluator.Default, SpecificationValidator.Default)
{
}
protected PaginationSpecification(IPagination pagination,
IInMemorySpecificationEvaluator inMemorySpecificationEvaluator)
: this(pagination, inMemorySpecificationEvaluator, SpecificationValidator.Default)
{
}
protected PaginationSpecification(IPagination pagination, ISpecificationValidator specificationValidator)
: this(pagination, InMemorySpecificationEvaluator.Default, specificationValidator)
{
}
protected PaginationSpecification(IPagination pagination,
IInMemorySpecificationEvaluator inMemorySpecificationEvaluator,
ISpecificationValidator specificationValidator, bool isTracking = false) : base(inMemorySpecificationEvaluator, specificationValidator)
{
if (pagination is IOrderRequestModel orderRequestModel)
{
OrderedSpecificationAdapt.Instance.ApplyOrder(Query, orderRequestModel);
}
Query.AsNoTracking().Skip(pagination.PageSize * (pagination.PageIndex - 1))
.Take(pagination.PageSize);
}
public bool IsPredicateLess() => !WhereExpressions.Any();
}
Query.AsNoTracking()
makes class PaginationSpecification<T>
is an untracked specification.
Now, I create a TrackingPaginationSpecification<T>
class that inherit from PaginationSpecification<T>
, so I can use TrackingPaginationSpecification<T>
to query pagination results and edit them paged.
public abstract class TrackingPaginationSpecification<T> : PaginationSpecification<T>
{
protected PaginationSpecification(IPagination pagination)
{
// make the query is tracked
// this will cause compile error, AsTracking() method does not exists.
Query.AsTracking();
}
}
What can we do in this situation?
We can support AsTracking like this:
In static class SpecificationBuilderExtensions
, add two methods
/// <summary>
/// If the entity instances are modified, this will be detected
/// by the change tracker.
/// </summary>
/// <param name="specificationBuilder"></param>
public static ISpecificationBuilder<T> AsTracking<T>(
this ISpecificationBuilder<T> specificationBuilder) where T : class
=> AsTracking(specificationBuilder, true);
/// <summary>
/// If the entity instances are modified, this will be detected
/// by the change tracker.
/// </summary>
/// <param name="specificationBuilder"></param>
/// <param name="condition">If false, the setting will be discarded.</param>
public static ISpecificationBuilder<T> AsTracking<T>(
this ISpecificationBuilder<T> specificationBuilder,
bool condition) where T : class
{
if (condition)
{
specificationBuilder.Specification.AsNoTracking = false;
}
return specificationBuilder;
}
In class AsNoTrackingEvaluator
, modify GetQuery<T>
method.
public IQueryable<T> GetQuery<T>(IQueryable<T> query, ISpecification<T> specification) where T : class
{
return specification.AsNoTracking ? query.AsNoTracking() : query.AsTracking();
}
In situation one, We can let the repository class control the query is tracked or not and ignore specification's AsNoTracking
.
protected virtual IQueryable<TEntity> ApplySpecification(ISpecification<TEntity> specification,
bool isTracked = true,
bool evaluateCriteriaOnly = false) where TEntity : class
{
var q = SpecificationEvaluator.GetQuery(EntitySet, specification, evaluateCriteriaOnly);
return isTracked ? q.AsTracking() : q.AsNoTracking();
}
So, What do we say? I still think that support AsTracking
will be nicer :)
@fiseni thoughts?
I think we had another request for AsTracking
, so let's add it. @codelovercc Do you want to create a PR?
Sure, I'd like to.
Should we rename class AsNoTrackingEvaluator
to TrackingEvaluator
? When it support AsTracking
and AsNoTracking
, this may be more suitable but it's a breaking change.
No, no need for that. You'll create a separate new file called AsTrackingEvaluator
.
Completed, PR #338