我们有一个自定义静态类,其中包含使用 Entity Framework Core 调用存储库时要使用的自定义
.Include
方法。我们希望 include 能够采用已提供的任何选择器,例如 i.Documents
,如下所示:
private readonly IRepository<Item> _itemRepository;
var item = await _itemRepository.GetAll()
.IncludeCheckingSecurity(i => i.Documents)
.SingleAsync();
如果存储库属于某种类型 (
IObjectWithSecurity
) 并且选择器属于某种类型(IEnumerable
的IObjectWithSecurity
),则添加一个额外的 .Where()
用于选择器过滤 上存在的属性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);
}
}
编辑
例如,对存储库的调用:
private readonly IRepository<Item> _itemRepository;
var item = await _itemRepository.GetAll()
.IncludeCheckingSecurity(i => i.Documents)
.SingleAsync();
实际上应该导致这样的结果:
private readonly IRepository<Item> _itemRepository;
var item = await _itemRepository.GetAll()
.IncludeCheckingSecurity(i => i.Documents.Where(a => a.AllowAccess))
.SingleAsync();
其中 i.Documents 是继承自 IObjectWithSecurity 类型的 Enumerable。并且AllowAccess是IObjectWithSecurity的一个属性:
public interface IObjectWithSecurity
{
public bool AllowAccess { get; set; }
}
我们尝试过创建额外的表达式并使用 lambda 来连接它们:
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);
但是没有成功,因为看起来这更适合连接 2 个 where 表达式,而不是成员访问表达式和其集合上的 where 表达式。
此扩展允许向
Include
表达式添加额外的安全过滤器:
public static class SecurityExtensions
{
public static IIncludableQueryable<TEntity, IEnumerable<TProperty>> IncludeCheckingSecurity<TEntity, TProperty>(this IQueryable<TEntity> query, Expression<Func<TEntity, IEnumerable<TProperty>>> property)
where TEntity : class
where TProperty : IObjectWithSecurity
{
// Define a template filter expression to apply access checking
Expression<Func<TEntity, IEnumerable<TProperty>, IEnumerable<TProperty>>> filterTemplate = (e, p) => p.Where(x => x.AllowAccess);
// Replace parameters in the template with those from the property expression
var newIncludeBody = ReplacingExpressionVisitor.Replace(filterTemplate.Parameters[0], property.Parameters[0], filterTemplate.Body);
newIncludeBody = ReplacingExpressionVisitor.Replace(filterTemplate.Parameters[1], property.Body, newIncludeBody);
// Create a new lambda expression using the modified include body
var newIncludeLambda = Expression.Lambda<Func<TEntity, IEnumerable<TProperty>>>(newIncludeBody, property.Parameters);
// Apply the modified include expression with security filtering
return query.Include(newIncludeLambda);
}
}