泛型EntityFramework核心表达式的强制转换



我非常喜欢我看到的用于筛选记录的IRecordAuthority概念。允许特定身份在EF DbContext上查询。接口声明Expression<Func<TEntity, bool>> Clause<TEntity>() where TEntity : IEntity,实现方式如下:

public class AssignedUserRecordAuthority : IRecordAuthority
{
private readonly IUserService _userService;
public AssignedUserRecordAuthority(IUserService userService)
{
_userService = userService;
}
public Expression<Func<TEntity, bool>> Clause<TEntity>() where TEntity : IEntity
{
if (typeof(TEntity) == typeof(Customer))
{
var name = _userService.ClaimsIdentity.Name;
return x => (x as Customer).ManagedBy == name;
}
return x => false;
}
}

在我的EntityDbContext上,有一个调用将此表达式插入上下文中的每个IQueryable请求:

public new IQueryable<TEntity> Get<TEntity>() where TEntity : class, IEntity
{
return Set<TEntity>()
.AsExpandable()
.Where(_recordAuthority.Clause<TEntity>());
}

然后,调用代码可以针对用例使用标准Linq。

我面临的问题是,我得到了这个错误:

系统。AggregateException:'出现一个或多个错误。(LINQ表达式"DbSet其中(c=>(c作为客户(。ManagedBy==__name_0('无法翻译。以可翻译的形式重写查询,或者通过插入对AsEnumerable((、AsAsyncEnumerable(、ToList((或ToListAsync((的调用,显式切换到客户端评估。看见https://go.microsoft.com/fwlink/?linkid=2101038了解更多信息。('

我只能假设EF正在尝试翻译演员阵容,但没有提供商。如果是字符串比较,我会很惊讶。因此,由于我将为每个实体类型添加的各种决策可能在很大程度上依赖于对每个实体的属性的访问进行比较,有人能看到我如何在表达式之外添加一个转换步骤,从而不会导致转换失败吗?

记录授权实现的目的是构建一组条件,这些条件是实现IEntity的特定实体类的表达式,以限制调用方可以查看的行。该表达式设置在调用方的表达式之前,调用方不能更改该表达式,因此提供了EF无法开箱即用的行级安全性。此类将返回x=>在底部为true,对于将应用限制的任何实体,将对TEntity类型进行if块检查,并在其中构建一个适合的表达式。在需要将表上的值与已知值进行比较的情况下,需要转换为特定实体类型,例如我的示例中的登录用户名,但它可以是任何值。

更新:带有工作示例的完整代码位于https://github.com/steveski/Perigee.Framework

要使代码正常工作,需要使用普通强制转换而不是安全强制转换。

在代码中实现这一点的最简单方法是进行双重转换:

public class AssignedUserRecordAuthority : IRecordAuthority
{
private readonly IUserService _userService;
public Expression<Func<TEntity, bool>> Clause<TEntity>() where TEntity : IEntity
{
if (typeof(TEntity) == typeof(Customer))
{
var name = _userService.ClaimsIdentity.Name;
// Changed from `x as Customer` to `(Customer)(object)x`:
return x => ((Customer)(object)x).ManagedBy == name; 
}
return x => false;
}
}

最新更新