【发布时间】:2023-03-08 06:27:01
【问题描述】:
在我目前正在进行的一个项目中,我们已经意识到我们不应该使用 DocumentDb 集合,就好像它们等同于 f.ex SQL Server 中的表一样。因此,我们现在将属于单个租户的所有实体保存在单个集合中。
我们的代码库中已经有很多 linq 查询,它们假设每种文档类型(聚合根)都保存在一个专用集合中。为了让过渡变得轻松,我着手重构我们的数据访问对象,以便其 api 继续推理聚合根,并在其实现中处理单个集合与专用集合。
我的方法是将聚合根包装在 Resource<T> 对象中,该对象派生自 Resource 并公开 Model 属性和 Type 属性。我想我将能够根据以下代码将IQueryable<T> 暴露给消费代码:
return _client.CreateDocumentQuery<Resource<TModel>>(_collection.DocumentsLink)
.Where(x => x.Type == typeof(TModel).Name)
.Select(x => x.Model);
初步测试表明,这按计划进行,我自信地提交了我的更改。然而,在进行功能测试时,我们发现一些查询模型的所有属性都设置为默认值(即 null、0、false 等)。
我可以用下面的代码重现这个问题:
var wrong = _client.CreateDocumentQuery<Resource<TModel>>(_collection.DocumentsLink)
.Where(x => x.Type == typeof(TModel).Name)
.Select(x => x.Model)
.Where(x => !x.IsDeleted)
.ToArray();
var correct = _client.CreateDocumentQuery<Resource<TModel>>(_collection.DocumentsLink)
.Where(x => x.Type == typeof(TModel).Name)
.Where(x => !x.Model.IsDeleted)
.Select(x => x.Model)
.ToArray();
以上查询的结果不一样!!
- 两个查询都返回相同数量的
TModel实例。 - 只有第二个示例返回的实例才会填充其属性。
为了使我的重构成功,我需要wrong 是...正确的:) 回退到 SQL 不是一种选择,因为我们重视 linq 的类型安全。更改我们公开Resource<T> 对象的方法会涉及大量代码,因为它需要将所有*.Property 引用替换为*.Model.Property 引用。
DocumentDb 客户端中的 linq 提供程序似乎存在问题。
我们使用 Microsoft.Azure.DocumentDb 1.4.1 版
编辑 2015-09-24
生成的 SQL 查询是:
- 正确:
{"query":"SELECT VALUE root.Model FROM root WHERE ((root.Type = \"DocumentType\") AND (NOT root.Model.IsDeleted)) "} - 错误:
{"query":"SELECT * FROM root WHERE ((root.Type = \"DocumentType\") AND (NOT root.Model.IsDeleted)) "}
此外,此问题已在 GitHub 上报告(并提取):https://github.com/Azure/azure-documentdb-net/issues/58
【问题讨论】:
-
两种情况下生成的 SQL 查询是什么?我敢打赌,第二个创建了一个简单的
WHERE子句,而第一个创建了一个奇怪的子查询。顺便说一句,SQL 是类型安全的。参数化 SQL 查询也是类型安全的。这是类型不安全的字符串连接。 -
不知道生成的SQL是什么样子的。我不知道如何确定。让我研究一下(今天晚些时候)。你的第二个陈述是什么意思?
-
最坏的情况,你可以使用 Fiddler 来捕获发送到数据库的 HTTP 请求
-
事实证明这几乎是不可能的...... Fiddler 没有显示任何查询流量。该代码不允许我检查生成的 SQL afaik。
-
您还可以通过在 LINQ 查询上调用
toString()来检查生成的 SQL。
标签: c# linq azure-cosmosdb