无法将 'MongoDB.Bson.BsonString' 类型的对象转换为 MongoDB .NET 驱动程序中的类型 'MongoDB.Bson.BsonDocument'
Unable to cast object of type 'MongoDB.Bson.BsonString' to type 'MongoDB.Bson.BsonDocument' in MongoDB .NET Driver
我在尝试使用 MongoDB .NET 客户端 运行 聚合管道时遇到问题。我的代码如下所示:
public async Task<IEnumerable<string>> GetPopularTags(int count)
{
var events = _database.GetCollection<Event>(_eventsCollectionName);
var agg = events.Aggregate();
var unwind = agg.Unwind<Event, Event>(e => e.Tags);
var group = unwind.Group(e => e.Tags, v => new { Tag = v.Key, Count = v.Count() });
var sort = group.SortByDescending(e => e.Count);
var project = group.Project(r => r.Tag);
var limit = project.Limit(count);
var result = await limit.SingleOrDefaultAsync();
return result;
}
(每个阶段的单独变量仅用于调试目的)
在尝试获取管道结果(最后一个变量)时出现以下错误:
System.InvalidCastException: Unable to cast object of type 'MongoDB.Bson.BsonString' to type 'MongoDB.Bson.BsonDocument'
我错过了什么?
在此先感谢您的帮助!
解决方案
我终于弄明白,我在最后一行遇到异常与错误所在无关。我在每个阶段都尝试 运行ning .SingleOrDefault()
来查看输出,我注意到我的管道有几个问题。
我的展开阶段试图 return 一个 Event
对象,但由于它正在展开 Tags
属性(这是一个 List<string>
),它试图将其设置为 string
并抛出异常。我通过让它将输出类型设置为默认值 BsonDocument
解决了这个问题,然后在下一阶段使用 ["Tags"]
访问器来获取我需要的值。它看起来像这样:
var dbResult = await events.Aggregate()
.Unwind(e => e.Tags)
.Group(e => e["Tags"], v => new { Tag = v.Key, Count = v.Count() })
我的项目阶段由于某种原因无法正常工作。我无法将 Tag
属性(原来是 BsonValue
类型)转换为 string
。最后我删除了那个阶段并将其替换为 dbResult.Select(t => t.Tag.AsString)
以将其转换为字符串。不是最优雅的解决方案,但总比没有好。
最后我的代码看起来像这样:
public async Task<IEnumerable<string>> GetPopularTags(int count)
{
var events = _database.GetCollection<Event>(_eventsCollectionName);
var dbResult = await events.Aggregate()
.Unwind(e => e.Tags)
.Group(e => e["Tags"], v => new { Tag = v.Key, Count = v.Count() })
.SortByDescending(e => e.Count)
.Limit(count)
.ToListAsync();
var result = dbResult.Select(t => t.Tag.AsString);
return result;
}
您遇到的问题基本上可以简化为以下代码行:
var agg = collection.Aggregate().Project(x => x.Tag);
其中 Tag
是模型中的 string
属性。
Aggregate()
和所有 MongoDB 驱动程序运算符似乎比 C# 语法允许的更接近聚合框架。
根据您的代码,result
变量应该是 String
类型,驱动程序将其转换为 MongoDB.Bson.BsonString
但是聚合框架总是 returns BSON文档(在本例中为单个文档),因此 MongoDB .NET 驱动程序无法在 运行 时间内处理此类反序列化 (BsonDocument -> BsonString
).
第一个解决方法很明显 - return 任何类似于 BSON 文档并且可以从 BsonDocument
类型反序列化的东西,如:
collection.Aggregate().Project(x => new { x.Tag });
然后将结果映射到内存中(相同的查询在幕后 运行)
另一种方法:使用 .AsQueryable()
将您的查询转换为 LINQ,这允许您以更灵活的方式 return 结果:
collection.AsQueryable().Select(x => x.Tag);
在这两种情况下,为我的投影生成的查询看起来是一样的:
{aggregate([{ "$project" : { "Tag" : "$Tag", "_id" : 0 } }])}
有点晚了,但这也有类似的问题,这对我来说已经解决了:
您需要创建一个中间 class 来代表组 { Tag = v.Key, Count = v.Count() }
,然后将项目更改为此。
.Project(Builders<YourIntermediateClass>.Projection.Expression(x => x.Tag))
我在尝试使用 MongoDB .NET 客户端 运行 聚合管道时遇到问题。我的代码如下所示:
public async Task<IEnumerable<string>> GetPopularTags(int count)
{
var events = _database.GetCollection<Event>(_eventsCollectionName);
var agg = events.Aggregate();
var unwind = agg.Unwind<Event, Event>(e => e.Tags);
var group = unwind.Group(e => e.Tags, v => new { Tag = v.Key, Count = v.Count() });
var sort = group.SortByDescending(e => e.Count);
var project = group.Project(r => r.Tag);
var limit = project.Limit(count);
var result = await limit.SingleOrDefaultAsync();
return result;
}
(每个阶段的单独变量仅用于调试目的)
在尝试获取管道结果(最后一个变量)时出现以下错误:
System.InvalidCastException: Unable to cast object of type 'MongoDB.Bson.BsonString' to type 'MongoDB.Bson.BsonDocument'
我错过了什么?
在此先感谢您的帮助!
解决方案
我终于弄明白,我在最后一行遇到异常与错误所在无关。我在每个阶段都尝试 运行ning .SingleOrDefault()
来查看输出,我注意到我的管道有几个问题。
我的展开阶段试图 return 一个
Event
对象,但由于它正在展开Tags
属性(这是一个List<string>
),它试图将其设置为string
并抛出异常。我通过让它将输出类型设置为默认值BsonDocument
解决了这个问题,然后在下一阶段使用["Tags"]
访问器来获取我需要的值。它看起来像这样:var dbResult = await events.Aggregate() .Unwind(e => e.Tags) .Group(e => e["Tags"], v => new { Tag = v.Key, Count = v.Count() })
我的项目阶段由于某种原因无法正常工作。我无法将
Tag
属性(原来是BsonValue
类型)转换为string
。最后我删除了那个阶段并将其替换为dbResult.Select(t => t.Tag.AsString)
以将其转换为字符串。不是最优雅的解决方案,但总比没有好。
最后我的代码看起来像这样:
public async Task<IEnumerable<string>> GetPopularTags(int count)
{
var events = _database.GetCollection<Event>(_eventsCollectionName);
var dbResult = await events.Aggregate()
.Unwind(e => e.Tags)
.Group(e => e["Tags"], v => new { Tag = v.Key, Count = v.Count() })
.SortByDescending(e => e.Count)
.Limit(count)
.ToListAsync();
var result = dbResult.Select(t => t.Tag.AsString);
return result;
}
您遇到的问题基本上可以简化为以下代码行:
var agg = collection.Aggregate().Project(x => x.Tag);
其中 Tag
是模型中的 string
属性。
Aggregate()
和所有 MongoDB 驱动程序运算符似乎比 C# 语法允许的更接近聚合框架。
根据您的代码,result
变量应该是 String
类型,驱动程序将其转换为 MongoDB.Bson.BsonString
但是聚合框架总是 returns BSON文档(在本例中为单个文档),因此 MongoDB .NET 驱动程序无法在 运行 时间内处理此类反序列化 (BsonDocument -> BsonString
).
第一个解决方法很明显 - return 任何类似于 BSON 文档并且可以从 BsonDocument
类型反序列化的东西,如:
collection.Aggregate().Project(x => new { x.Tag });
然后将结果映射到内存中(相同的查询在幕后 运行)
另一种方法:使用 .AsQueryable()
将您的查询转换为 LINQ,这允许您以更灵活的方式 return 结果:
collection.AsQueryable().Select(x => x.Tag);
在这两种情况下,为我的投影生成的查询看起来是一样的:
{aggregate([{ "$project" : { "Tag" : "$Tag", "_id" : 0 } }])}
有点晚了,但这也有类似的问题,这对我来说已经解决了:
您需要创建一个中间 class 来代表组 { Tag = v.Key, Count = v.Count() }
,然后将项目更改为此。
.Project(Builders<YourIntermediateClass>.Projection.Expression(x => x.Tag))