Add support for EFCore Include() Linq extension.
Closed this issue · 5 comments
Maybe we allow the abstaction to leak in the Specification or the QueryOptions. Either way, we need to extend one of them to support storage specific Linq extension methods.
After the integration of the LiteDB.Queryable package, which supports Include extensions, we can refactor the LiteRepository to use the LinqRepository as ist base class. Then we can extend the IQueryOptions with an Include operation.
I'm just also pasting this here because i'm not sure if you see things in the comments of commits:
Maybe you might find this helpful for your .Include
Queries - I do something like this:
protected sealed override IAsyncEnumerable<TProjected> FindManyAsync<TProjected>(
EfTrackingOptions asNoTracking,
Expression<Func<TEntity, bool>> whereExpression,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> includeExpression,
Expression<Func<TEntity, TProjected>> projectExpression,
CancellationToken cancellationToken = default)
{
return this.ToListAsync(
this.Queryable.Apply(asNoTracking)
.Apply(whereExpression)
.Apply(includeExpression)
.ToProjection(projectExpression),
cancellationToken);
}
Then you can write the include statement like this:
Func<IQueryable<Book>, IIncludableQueryable<Book, object>> include = a =>
a.Include(i => i.BookAuthors).ThenInclude(_ => _.Author);
var randomBook = this.GetRandomBook();
var shouldIncludeRelations = this._bookRepository.GetById(EfTrackingOptions.WithTracking,
randomBook.Id, include );
or like this:
IIncludableQueryable<Book, object> IncludeBookAuthorsAndAuthors(IQueryable<Book> a)
{
return a.Include(i => i.BookAuthors)
.ThenInclude(_ => _.Author);
}
var randomBook = this.GetRandomBook();
var shouldIncludeRelations = this._bookRepository
.GetById(EfTrackingOptions.WithTracking, randomBook.Id, IncludeBookAuthorsAndAuthors);
This way that I shared can be improved in my opinion - I would like to put it into an ISpecification<T>
and/or make some sort of fluent builder interface so you could do something like:
var includeStatement = RelationsBuilder.Initialize<Book>()
.Include(i => i.BookAuthors)
.ThenInclude(_ => _.Author);
- Copied from comment: 80680c2#commitcomment-92084357
This way its easier to type. I think including it in a ISpecification<T>
is probably a good solution
If your curious, the .Apply
code for the include statement is this:
public static IQueryable<T> Apply<T>(
this IQueryable<T> queryable,
Func<IQueryable<T>, IIncludableQueryable<T, object>> includeExpression)
{
return includeExpression(queryable);
}
- Copied from comment: 80680c2#commitcomment-92084513
I just wanted to share this! Thanks!
Is there any reason why you didn't use IIncludableQueryable<T,K>
in your implementation? Just curious
Is there any reason why you didn't use
IIncludableQueryable<T,K>
in your implementation? Just curious
Mostly to keep the IQueryable
away from the user and the fact that not every storage may support LINQ/IQueryable
.
Mostly to keep the
IQueryable
away from the user and the fact that not every storage may support LINQ/IQueryable
.
Both of those are very good reasons, now I realize my error with allowing IIncludableQueryable<T,K>
to leak haha, I will need to fix that. Also very good point about storage providers and IQueryable
.
Good thinking of course!