0

I need to make generic multiple include function as a service with generic repository. But unfortunately, I get nothing !!

here is my attempt using aggregate linq.

    public IQueryable<TEntityDTO> getRowsWithIncludeMultiple(int page = 0, params Expression<Func<TEntityDTO, object>>[] includes)
    {
        GridSetting gs = GetGrid();
        IEnumerable<TEntity> getPage = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage);
        IQueryable<TEntityDTO> rows = _mapper.Map<IEnumerable<TEntityDTO>>(getPage).AsQueryable();
        if (includes != null) { rows = includes.Aggregate(rows, (current, include) => current.Include(include)); }

        // or
        //foreach (var include in includes)
        //{
        //    rows = rows.Include(include);
        //}

        return rows;
    }

when I add debugging point I get that the includes has list of expression

when I add debugging point I get that the includes has list of expression

and then here is how I use it

var xxx = _customerService.getRowsWithIncludeMultiple(page: 0, i => i.cityDTO, i => i.ageDTO);

the problem here I get customers without the included things (cityDTO & ageDTO)

the problem here I get customers without the included things (cityDTO & ageDTO)

let me include here models

public class CustomerDTO
{
    public int Id { get; set; }
    public string CustName { get; set; }
    public string CustJobTitle { get; set; }
    public string CustAge { get; set; }
    public bool IsManager { get; set; }

    // FKs
    public int AgeId { get; set; }
    public int CityId { get; set; }

    public AgeDTO ageDTO { get; set; }
    public CityDTO cityDTO { get; set; }
}

public class CityDTO
{
    public int Id { get; set; }
    public string CityName { get; set; }
    public List<CustomerDTO> customerDTO { get; set; }
}


public class AgeDTO
{
    public int Id { get; set; }
    public int AgeName { get; set; }
    public List<CustomerDTO> customerDTO { get; set; }
}

this is how DB looks like

Update ... showing the whole service, usage, and injection

here is the whole generic repository service and how it looks like

public class Repository<TEntity, TEntityDTO> : IRepository<TEntity, TEntityDTO> where TEntity : class where TEntityDTO : class
{

    protected readonly AppDbContext _context;
    private readonly DbSet<TEntity> _dbSet;
    private readonly IMapper _mapper;

    public Repository(AppDbContext context, IMapper mapper)
    {
        _context = context;
        _dbSet = context.Set<TEntity>();
        _mapper = mapper;
    }


 // GENERIC CRUD ... 

 // and then here where i want to focus  
 
         public IQueryable<TEntityDTO> getRowsWithIncludeMultiple(int page = 0, params Expression<Func<TEntityDTO, object>>[] includes)
    {
        GridSetting gs = GetGrid();
        IEnumerable<TEntity> getPage = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage);
        IQueryable<TEntityDTO> rows = _mapper.Map<IEnumerable<TEntityDTO>>(getPage).AsQueryable();
        if (includes != null) { rows = includes.Aggregate(rows, (current, include) => current.Include(include)); }

        // or
        //foreach (var include in includes)
        //{
        //    rows = rows.Include(include);
        //}

        return rows;
    }

}

and then here is how customer service uses generic repo

public class CustomerService : Repository<Customer, CustomerDTO>, ICustomerService
{
    public CustomerService(AppDbContext db, IMapper mapper) : base(db, mapper) { }
}

finally injection in Program.cs

        builder.Services.AddScoped(typeof(IRepository<,>), typeof(Repository<,>));

        builder.Services.AddScoped<ICustomerService, CustomerService>();
  • Do it before `_mapper.Map`. It is later for EF Core. – Svyatoslav Danyliv Feb 07 '23 at 15:05
  • 1
    Actually you cannot apply `Include` to DTO object. It works only with entity classes. What you can do it is use `ProjectTo` instead of `Map` and use [Explicit Expansion](https://docs.automapper.org/en/stable/Queryable-Extensions.html#explicit-expansion). Offtop: do you have feeling that your generic repository is useless and become a monster? – Svyatoslav Danyliv Feb 07 '23 at 15:35
  • If this is AutoMapper, use `ProjectTo`, not `Include`. The projection fully determines which dto properties are populated. Side not, this `AsQueryable()` call is a typical example of what I describe [here, under "What do these methods NOT do?"](https://stackoverflow.com/a/17996264/861716). – Gert Arnold Feb 07 '23 at 19:12

1 Answers1

-1

thanks @Svyatoslav Danyliv and thanks for all contributors your answer helped me a lot.

cannot apply Include to DTO object

so, I would to share what I have made so far.

instead of

public IQueryable<TEntityDTO> getRowsWithIncludeMultiple(int page = 0, params Expression<Func<TEntityDTO, object>>[] includes)
{
    GridSetting gs = GetGrid();
    IEnumerable<TEntity> getPage = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage);
    IQueryable<TEntityDTO> rows = _mapper.Map<IEnumerable<TEntityDTO>>(getPage).AsQueryable();
    if (includes != null) { rows = includes.Aggregate(rows, (current, include) => current.Include(include)); }

    return rows;
}

I applied include to the original entity not to DTO

    public IEnumerable<TEntityDTO> IncludeMultiple(int page = 0, params Expression<Func<TEntity, object>>[] includes)
    {
        GridSetting gs = GetGrid();
        IQueryable<TEntity> rows = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage).AsQueryable();

        if (includes != null)
        { rows = includes.Aggregate(rows, (current, include) => current.Include(include).AsQueryable().AsNoTracking()); }

        return _mapper.Map<IEnumerable<TEntityDTO>>(rows).AsQueryable();
    }

And here is some clarification to make the idea more understandable

    public IEnumerable<TEntityDTO> IncludeMultiple(params Expression<Func<TEntity, object>>[] includes)
    {
        // get IQueryable of TEntity
        IQueryable<TEntity> rows = _dbSet.AsQueryable();

        // and here using `linq Aggregate` to append multiple include statement 
        if (includes != null)
        { rows = includes.Aggregate(rows, (current, include) => current.Include(include).AsQueryable().AsNoTracking()); }

        // here I use auto mapper to map IEnumerable object to IEnumerable object as a result
        return _mapper.Map<IEnumerable<TEntityDTO>>(rows).AsQueryable();
    }

and finally the usage

_ServiceName.IncludeMultiple(i => i.city, i => i.age);
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 14 '23 at 00:24