nested Includes in specification pattern

55 Views Asked by At

So I'm using specification pattern, and I came across use case where I need to use then includes to include a navigation property inside my Entities to get it with the queries and I did not find a good solution for this issue.


public class BaseSpecifications<T> : ISpecifications<T> where T : class
{
    public Expression<Func<T, bool>> Criteria { get; set; }  // represents the where clause
    public List<Expression<Func<T, object>>> Includes { get; set; } = new List<Expression<Func<T, object>>>();
    public List<string> IncludeStrings { get; set; } = new List<string>();

    public BaseSpecifications() // when not having any criteria aka where clause
    {

    }

    public BaseSpecifications(Expression<Func<T, bool>> criteriaExpression)
    {
        Criteria = criteriaExpression;
    }
    protected virtual void AddInclude(Expression<Func<T, object>> includeExpression)
    {
        Includes.Add(includeExpression);
    }

}

public class ServiceWithProvidersSpecification : BaseSpecifications<Service>
{
    public ServiceWithProvidersSpecification()
    {

        AddInclude(x => x.ProviderServices);
        //AddInclude( x = > x.ProviderServices.Provider);  Thats what i want to include 
    }

    public ServiceWithProvidersSpecification(string serviceId) : base(s => s.ServiceID == serviceId)
    {
        AddInclude(x => x.ProviderServices);
     //AddInclude( x = > x.ProviderServices.Provider); Thats what i want to include 
    }

}
public class Service
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public string ServiceID { get; set; }
    public List<ProviderService> ProviderServices { get; set; } = new List<ProviderService>();
}
 public class ProviderService
 {
     public string ProviderID { get; set; }
     public string ServiceID { get; set; }

     public decimal Price { get; set; }

     [ForeignKey("ProviderID")]
     public Provider Provider { get; set; }

     [ForeignKey("ServiceID")]
     public Service Service { get; set; }

     public List<ServiceRequest> ServiceRequest { get; set; }
 }

I tried to use strings but I guess this is not a reliable solution

1

There are 1 best solutions below

0
Shrembo On

Your BaseSpecifications is not set up for nested includes.

public class BaseSpecifications<T> : ISpecifications<T> where T : class
{
    public List<Func<IQueryable<T>, IIncludableQueryable<T, object>>> NestedIncludes { get; } 
    = new List<Func<IQueryable<T>, IIncludableQueryable<T, object>>>();

    protected virtual void AddNestedInclude(Func<IQueryable<T>, IIncludableQueryable<T, object>> nestedIncludeExpression)
    {
        NestedIncludes.Add(nestedIncludeExpression);
    }
}
public class ServiceWithProvidersSpecification : BaseSpecifications<Service>
{
    public ServiceWithProvidersSpecification()
    {
        AddInclude(x => x.ProviderServices);
        AddNestedInclude(query => query.Include(x=> x.ProviderServices).ThenInclude(ps => ps.Provider));
    }

    public ServiceWithProvidersSpecification(string serviceId) : base(s => s.ServiceID == serviceId)
    {
        AddInclude(x => x.ProviderServices);
        AddNestedInclude(query => query.Include(x => x.ProviderServices).ThenInclude(ps => ps.Provider));
    }
}

Modify the Query Logic: Wherever you're executing the query, ensure it uses both Includes and NestedIncludes.

IQueryable<T> query = ...;

foreach (var include in spec.Includes)
{
    query = query.Include(include);
}


//Apply nested includes

foreach (var nestedInclude in spec.NestedIncludes)
{
    query = nestedInclude(query);
}