向自定义 Entity Framework Core 中的通用表达式添加 where .Include

问题描述 投票:0回答:1

我们有一个自定义静态类,其中包含使用 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 表达式。

c# asp.net-core entity-framework-core
1个回答
0
投票

此扩展允许向

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);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.