Невозможно привести объект типа «MongoDB.Bson.BsonString» к типу «MongoDB.Bson.BsonDocument» в MongoDB. NET Драйвер - PullRequest
2 голосов
/ 10 февраля 2020

Я столкнулся с проблемой при попытке запустить конвейер агрегации с использованием 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: невозможно преобразовать объект типа 'MongoDB.Bson.BsonString' в тип 'MongoDB.Bson.BsonDocument'

Чего мне не хватает ?

Заранее спасибо за любую помощь!

РЕШЕНИЕ

Я наконец понял, что факт, что я получаю исключение в последней строке не имеет ничего общего с тем, где была ошибка. Я попытался запустить .SingleOrDefault() на каждом этапе, чтобы увидеть выходные данные, и заметил, что у моего конвейера было несколько проблем.

  1. Мой этап отката пытался вернуть объект 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() })
    
  2. Мой этап проекта по какой-то причине не работал. Мне не удалось преобразовать свойство 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;
    }

1 Ответ

3 голосов
/ 10 февраля 2020

Проблема, с которой вы сталкиваетесь, может быть в основном упрощена до строки кода ниже:

var agg = collection.Aggregate().Project(x => x.Tag);

Где Tag - это свойство string в вашей модели.

Похоже, что Aggregate() и все операторы драйвера MongoDB ближе к Aggregation Framework, чем позволяет им синтаксис C#.

На основе вашего кода предполагается, что переменная result имеет тип String, который переводится драйвером в MongoDB.Bson.BsonString, однако Aggregation Framework всегда возвращает документы BSON (в данном случае один), поэтому MongoDB . NET драйвер не может обработать такую ​​десериализацию во время выполнения (BsonDocument -> BsonString).

Первый обходной путь очевиден - возвращает все, что напоминает документ BSON и может быть десериализовано из типа BsonDocument, например:

collection.Aggregate().Project(x => new { x.Tag });

, а затем сопоставить результаты в памяти (тот же запрос выполняется за кулисами)

Другой подход: перевести запрос в LINQ, используя .AsQueryable(), что позволяет более гибко возвращать результаты:

collection.AsQueryable().Select(x => x.Tag);

В обоих случаях запрос, сгенерированный для моей проекции, выглядит одинаково:

{aggregate([{ "$project" : { "Tag" : "$Tag", "_id" : 0 } }])}
...