我无法创建请求来获取姓名如“param”的朋友列表。主要要求是请求必须接受所有带有名字或姓氏参数的朋友,而不是全部,然后做
where
。
异常返回到使用'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'
,但它确实在我的where
之前请求,这是个坏主意,因为如果有很多朋友,那么这是一个很大的负载。
FriendshipRepository 中的方法
public IQueryable<User> GetAllFriends(int id)
{
var friends = _socialNetworkDbContext.Friends
.Include(f => f.FriendUser.Profile)
.Where(f => f.UserId == id || f.FriendId == id)
.Select(f => f.UserId == id ? f.FriendUser : f.User)
.AsQueryable();
return friends;
}
然后在通过名字或姓氏查找朋友的服务方法中有这个请求
string name = parts[0].ToLower();
matchingUsers = _friendshipRepository.GetAllFriends(userDb.Id)
.Where(user => EF.Functions.Like( user.Profile.Name.ToLower(), "%"+name+"%")
|| EF.Functions.Like( user.Profile.Surname.ToLower(), "%"+name+"%"))
.ToList();
还有一部分
.Where(user => EF.Functions.Like( user.Profile.Name.ToLower(), "%"+name+"%")
|| EF.Functions.Like( user.Profile.Surname.ToLower(), "%"+name+"%"))
调用异常
could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
所以我需要做的请求不是向所有人发出请求,而是通过我的“where”参数进行请求。
'AsEnumerable', 'AsAsyncEnumerable', 'ToList' after calling GetAllFriends(userDb.Id)
是个坏主意,因为如果有很多朋友,那么这是一个很大的负担。
所以这样做可能是正常的,我不知道,可能是执行此请求的唯一方法?
我不确定 EF 是否可以正确处理这个问题:
.Select(f => f.UserId == id ? f.FriendUser : f.User)
并且仍然将其视为可扩展的
IQueryable
,但是 EF 可以使用 LIKE %term%
构建 string.Contains
查询。
你可以尝试:
public IQueryable<User> GetAllFriendUsers(int id)
{
var query = _socialNetworkDbContext.Friends
.Where(f => f.UserId == id || f.FriendId == id)
.Select(f => f.UserId == id ? f.FriendUser : f.User);
return query;
}
然后:
string name = parts[0].ToLower();
matchingUsers = _friendshipRepository.GetAllFriendUsers(userDb.Id)
.Where(u => u.Profile.Name.Contains(name)
|| u.Profile.Surname.Contains(name))
.ToList();
值得注意的是:如果返回
IQueryable
并使用投影 /w Select
,我们不需要 Include
语句。调用者可以自由地投影或急切加载他们特别需要的相关数据。我们也不需要将其转换为 AsQueryable
,因为它会返回 IQueryable
。
此处需要注意的是,您的数据库针对姓名和姓氏设置了不区分大小写的排序规则(即 CI* 排序规则)。如果排序规则区分大小写 (CS*),则在
.ToLower()
之前添加 .Contains(...)
。
如果查询仍然不起作用,那么您可能需要调整存储库以返回好友,然后构建子查询来检查两个用户引用:
public IQueryable<Friend> GetAllFriends(int id)
{
var query = _socialNetworkDbContext.Friends
.Where(f => f.UserId == id || f.FriendId == id);
return query;
}
string name = parts[0].ToLower();
matchingUsers = _friendshipRepository.GetAllFriends(userDb.Id)
.Where(f => (f.UserId == userDb.Id
&& (f.User.Profile.Name.Contains(name)
|| f.User.Profile.Surname.Contains(name)))
|| (f.FriendId == userDb.Id
&& (f.FriendUser.Profile.Name.Contains(name)
|| f.FriendUser.Profile.Name.Contains(name))))
.Select(f => f.FriendId == userDb.Id ? f.FriendUser : f.User)
.ToList();
这里的区别在于我们返回 Friends,然后构建查询表达式来检查 User name 或 FriendUser name,具体取决于哪个 Id 匹配。
请注意,因为您想要有条件地加载 Friend.User 或 Friend.FriendUser,如果您想要立即加载相关详细信息(例如适用用户的个人资料),这将使事情变得复杂。例如,您可以尝试以下操作:
.Include(f => f.FriendId == userDb.Id ? f.FriendUser.UserProfile : f.User.UserProfile)
但是我不相信
Include
中的那种条件有效。
如果读取数据并且您需要用户和用户配置文件详细信息,我建议使用
Select
投影视图模型,以便您可以从适用的用户和用户配置文件中选择详细信息。否则,我建议将存储库调用和业务逻辑设计为明确说明所使用的 ID 是 FriendId 还是 UserId,而不是尝试在单个方法中进行有条件的选择。如果您的代码知道它正在根据朋友 ID 或用户 ID 加载用户,那么您可以确定要预先加载的实体和相关实体。作为一般规则,应尽可能避免类似的条件逻辑,因为它会使代码更难以一眼理解,并为隐藏的小错误敞开大门。