We have a custom static class containing a custom .Include method to be used when using Entity Framework Core to call a repository. We would like the include to take whatever selector has been provided, for example i.Documents as shown here:
private readonly IRepository<Item> _itemRepository;
var item = await _itemRepository.GetAll()
.IncludeCheckingSecurity(i => i.Documents)
.SingleAsync();
And if the repository is of a certain type (IObjectWithSecurity) and the selector is of a certain type (IEnumerable of IObjectWithSecurity) to add an additional .Where() for the selector filtering on a property that exists on IObjectWithSecurity:
public static IIncludableQueryable<TRepositoryType, TThisInclude> IncludeCheckingSecurity<TRepositoryType, TThisInclude>(
this IQueryable<TRepositoryType> source,
Expression<Func<TRepositoryType, TThisInclude>> propertySelector)
where TRepositoryType : class
{
var previousType = typeof(TRepositoryType);
var thisInclude = typeof(TThisInclude);
if (typeof(IObjectWithSecurity).IsAssignableFrom(previousType) && typeof(IEnumerable<IObjectWithSecurity>).IsAssignableFrom(thisInclude))
{
// Want to add a where to 'propertySelector' which filters it on a property within IObjectWithSecurity (which we have proved it is above)
return source.Include(propertySelector);
}
}
EDIT
For example, this call to the repository:
private readonly IRepository<Item> _itemRepository;
var item = await _itemRepository.GetAll()
.IncludeCheckingSecurity(i => i.Documents)
.SingleAsync();
Should actually result in this:
private readonly IRepository<Item> _itemRepository;
var item = await _itemRepository.GetAll()
.IncludeCheckingSecurity(i => i.Documents.Where(a => a.AllowAccess))
.SingleAsync();
Where i.Documents is an Enumerable of a type that inherits from IObjectWithSecurity. And AllowAccess is a property on IObjectWithSecurity:
public interface IObjectWithSecurity
{
public bool AllowAccess { get; set; }
}
We have experimented with creating additional expressions and using lambda to join them:
Expression<Func<TThisInclude, bool>> whereToAppend = e => ((IObjectWithSecurity)e).AllowAccess;
var newIncludeWithWhere = Expression.Lambda<Func<TRepositoryType, TThisInclude>>(Expression.AndAlso(propertySelector, whereToAppend), propertySelector.Parameters);
return source.Include(newIncludeWithWhere);
But without success as it seems this is more designed for joining 2 where expressions instead of a member access expression and a where expression on its collection.
DbSet. It always strikes me as a bit weird that so many people bury EF DbSets in a repository and then jump through hoops and loops to dig all kinds of tunnels to it. All under the guise of abstraction, which is immediately broken by EF-specific methods like this.