我正在编写一个密码查询,我想找到在给定日期范围内担任特定角色的所有用户。看起来
null
查询有点棘手。
考虑以下代码:
public async Task<Result<IEnumerable<UserNode>>> FindUsersByRoleNameAsync(
string roleName,
DateOnly? from = default,
DateOnly? until = default,
CancellationToken token = default)
{
from ??= DateOnly.MinValue;
until ??= DateOnly.MaxValue;
return await client.Cypher
.Match("(user:User)-[edge:has_role]-(role:Role)")
.Where((RoleNode role) => role.Name == roleName)
.AndWhere((HasRole edge) => edge.From >= from)
.AndWhere((HasRole edge) => edge.Until <= until) // this line is problematic!
.ToNodesAsync<UserNode>("user", UnitOfWork, token) // Custom deserialization...ignore.
.ConfigureAwait(false);
}
故障解释...
DateOnly?
参数 from
和 until
,可以在查询中省略。from
,则将其设置为 DateOnly.MinValue
(实际上,在 01/01/0001 之前没有人会拥有角色)until
,则设置为 DateOnly.MaxValue
(实际上,在 31/12/9999 之后没有人会拥有角色)一些上下文可能有助于解释为什么
null
似乎是问题所在;
HasRole.From
是DateOnly
。HasRole.Until
是 DateOnly?
,其中 null
表示该角色仍然适用。如果我从查询中省略这一行...
.AndWhere((HasRole edge) => edge.Until <= until) // this line is problematic!
...那么它适用于省略
from
和 until
的查询,以及 from
not 省略的查询。
似乎不起作用的一点是当尝试将
edge.Until
(可能是数据库中的null
)与until
(这将是指定值,或者如果省略则为DateOnly.MaxValue
)进行比较时.
删除上面的行,2/3 的查询测试通过。 重新添加该行会导致 0/3 查询测试通过。
我做错了什么?
值得查看从代码生成的实际查询,它类似于:
MATCH (user: User)-[edge:has_role]-(role:Role)
WHERE (role.Name = "Blah")
AND (edge.From >= "")
AND (edge.Until <= "9999-12-31")
所以它总是期待
Until
存在。您需要将查询更改为:
MATCH (user:User)-[edge:has_role]-(role:Role)
WHERE (role.Name = "Blah")
AND (edge. From >= "")
AND (edge. Until IS NULL)
OR (edge. Until <= "9999-12-31")
客户端正在按照您的要求进行操作,找到任何
HasRole
属性小于 Until
的 DateOnly.MaxValue
边,并写入相应的 Cypher。
也许这样的东西就是你想要的:
public async Task<Result<IEnumerable<UserNode>>> FindUsersByRoleNameAsync(
string roleName,
DateOnly? from = default,
DateOnly? until = default,
CancellationToken token = default)
{
from ??= DateOnly.MinValue;
until ??= DateOnly.MaxValue;
return await client.Cypher
.Match("(user:User)-[edge:has_role]-(role:Role)")
.Where((RoleNode role) => role.Name == roleName)
.AndWhere((HasRole edge) => edge.From >= from)
.AndWhere((HasRole edge) => edge.Until <= until) // this line is problematic!
.OrWhere($"edge.{nameof(HasRole.Until)} IS NULL") //Add here
.ToNodesAsync<UserNode>("user", UnitOfWork, token) // Custom deserialization...ignore.
.ConfigureAwait(false);
}