我有4个表。
1 - Users2 - UserRole3 - Role4 - AccessLevel。
我需要的是,当用户登录到应用程序时,访问级别列表将被发送到它。
我正在使用下面的代码来实现它。
var userInfo = await Users.Where(x => x.Id == id)
.Select(x => new
{
UserDusplayName = x.Name + x.Family,
AccessLevel = x.UserRoles.FirstOrDefault().Role.AccessLevels.ToList()
})
.Select(v=>new UserInformationDto
{
DispayName=v.UserDusplayName,
AccessUnserInfos=v.AccessLevel.Select(x=>x.Access)
}).FirstOrDefaultAsync();
在我的数据库中这个id有一个AccessLevel。
然而,当我向这个ID发送一个请求时,它显示出以下内容。
当从'VisitLambda'调用时,重写一个类型为'System.Linq.Expressions.ParameterExpression'的节点必须返回一个相同类型的非空值。或者,覆盖'VisitLambda'并将其改为不访问该类型的子节点。
我的代码有什么问题? 我怎么能解决这个问题?
我的代码有什么问题?
技术上的问题不是你的代码,这是一个有效的LINQ查询,但EF核心查询翻译器bugsshortcomings。
这里有两个结构
AccessLevel = x.UserRoles.FirstOrDefault().Role.AccessLevels.ToList()
导致EF Core翻译器出现问题。
首先是 ToList()
调用。虽然它在最终预测中得到了支持(Select
),它会导致进一步的查询操作符(如 next Select
部分
v.AccessLevel.Select(x=>x.Access)
二是 FirstOrDefault()
此处
x.UserRoles.FirstOrDefault().Role.AccessLevels
即把一个序列转换为单项,然后再从中抽取子序列。
我怎样才能解决这个问题?
首先,删除 ToList()
或取消中间环节 Select
. 第二,去除 FirstOrDefault()
- 它没有意义,或者如果真的需要,用等价的集限制运算符来代替。Take(1)
. 使用集平运算符 SelectMany
以获得所需的子集。
如
AccessLevel = x.UserRoles.SelectMany(ur => ur.Role.AccessLevels)
应该可以解决当前的问题。
不相关,但由于在你的模型中,用户有许多角色,而角色有许多访问级别,这里的
AccessUnserInfos = v.AccessLevel.Select(x => x.Access)
你可能会得到重复的值,所以考虑应用 Distinct
经营者
AccessUnserInfos = v.AccessLevel.Select(x => x.Access).Distinct()