使用Azure DocumentDB(CosmosDB).Net SDK

bc3*_*ech 1 azure azure-cosmosdb

看到这个问题后,我不确定为什么DocDb实例的以下代码不起作用

var userApps = _docs.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.USER_APPS),
        new SqlQuerySpec(@"SELECT r.appId FROM ROOT r WHERE r.userId = @userId", (@"@userId", userId).ToSqlParameters()))
    .ToList()
    .Select(s => (string)s.appId);
var query = _docs.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.APP_DEFINITIONS),
        new SqlQuerySpec(@"SELECT r.id, r.appName FROM ROOT r WHERE r.appId IN (@userApps)", (@"@userApps", userApps.ToArray()).ToSqlParameters()),
        new FeedOptions { EnableCrossPartitionQuery = true })
    .AsDocumentQuery();
Run Code Online (Sandbox Code Playgroud)

当我执行此操作时,尽管我知道数据应该返回我一个结果集,但每次它都返回为空。

到目前为止的故障排除

的变体 .Select()

将我输入的字符串返回string.Join到以逗号分隔的列表中。

例如:

var userApps = string.Join(@",", _docs.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.USER_APPS),
        new SqlQuerySpec(@"SELECT r.appId FROM ROOT r WHERE r.userId = @userId", (@"@userId", userId).ToSqlParameters()))
    .ToList()
    .Select(s => $@"'{s.appId}'");
Run Code Online (Sandbox Code Playgroud)

不要封装IN参数

在查询中删除参数规范周围的()可能是SqlParameter规范正在执行数组规范?

例如:@"SELECT r.id, r.appName FROM ROOT r WHERE r.appId IN @userApps"

最终抛出“语法错误,'@ userApps'附近的语法不正确”。

通过Azure门户查询进行验证

使(预期的)SQL运行该代码。

我得到的预期结果没有问题(即:我知道这些查询的结果集是书面的)。

查询1的调试器输出

AppIds 未来从查询1回。

解决方法不令人满意

将查询2更改为参数化。而是将查询1中以逗号分隔的ID列表注入其中:

var userApps = string.Join(@",", _docs.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.USER_APPS),
        new SqlQuerySpec(@"SELECT r.appId FROM ROOT r WHERE r.userId = @userId", (@"@userId", userId).ToSqlParameter()))
    .ToList()
    .Select(s => $@"'{s.appId}'"));
var query = _docs.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.APP_DEFINITIONS),
        new SqlQuerySpec($@"SELECT r.id, r.appName FROM ROOT r WHERE r.appId IN ({userApps})"),
        new FeedOptions { EnableCrossPartitionQuery = true })
    .AsDocumentQuery();
Run Code Online (Sandbox Code Playgroud)

可以完美地工作,但是我不会接受它作为此问题的答案,因为它与几十年来价值不菲的SQL最佳实践相抵触,坦率地说,不应该将其作为解决方案。


ToSqlParameters()如果是罪魁祸首,这是我的扩展方法(尽管这在我使用过的其他地方都可以使用。也许数组需要一些特殊的东西吗?):

public static SqlParameterCollection ToSqlParameters(this (string, object) parmDef) => new SqlParameterCollection(new[] { new SqlParameter(parmDef.Item1, parmDef.Item2) });
Run Code Online (Sandbox Code Playgroud)

谢谢!

Sam*_*hra 5

如果使用参数化的IN列表,则在扩展参数时它将被视为单个值。

例如,对于该查询:SELECT * FROM r WHERE r.item IN(@items)而@items定义为“ [['val1','val2','val3']””,则该查询将被解释为: SELECT * FROM r WHERE r.item IN(['val1','val2','val3'])这基本上意味着您正在将r.item与一个包含三个元素的数组的单个值进行比较(即等于r.item = ['val1','val2','val3'])。

要与多个项目进行比较,您需要为每个值使用一个参数。像这样:SELECT * FROM r WHERE r.item IN(@ val1,@ val2,@ val3])

编写此查询的一种更方便的方法是改用ARRAY_CONTAINS并将项目数组作为单个参数传递。因此,上面的查询将这样写:SELECT * FROM r WHERE ARRAY_CONTAINS(@items,r.item)