Как создать метод c BulkUpsertAsyn c для MongoDB в C#? - PullRequest
1 голос
/ 12 марта 2020

Я пытаюсь создать метод расширения для IMongoCollection<TDocument> в C#, который позволит перенести любой List<TDocument> в коллекцию MongoDB с помощью upsert. Я нашел другие статьи, которые предлагают List<WriteModel<TDocument>> в сочетании с BulkWriteAsyn c для выполнения этих операций в пакете.

Не в формате c, я могу добавить серию записей (в данном случае List<Line>), используя:

public static async Task<BulkWriteResult<Line>> BulkUpsertAsyncNonGeneric(this IMongoCollection<Line> collection, List<Line> entries)
        {
            var bulkOps = new List<WriteModel<Line>>();

            foreach (var entry in entries)
            {
                var filter = Builders<Line>.Filter.Eq(doc => doc.Id, entry.Id);

                var upsertOne = new ReplaceOneModel<Line>(filter, entry) { IsUpsert = true };

                bulkOps.Add(upsertOne);
            }

            return await collection.BulkWriteAsync(bulkOps);
        }

Путем изменения <Line> для <TDocument> Я сделал это частично обобщенно c, но есть предположение, что каждый TDocument имеет поле Id и что каждый entry in entries также имеет поле Id. Конечно, TDocument не имеет участников. Я хочу сделать эти определения полей полностью обобщенными c, в идеале, используя лямбду для соответствия формату вызова Filter.Eq(doc => doc.Id, entry.Id). Тем не менее, я застрял. Я действительно хочу избежать простой передачи строкового литерала с именами полей, что, я считаю, будет работать нормально, но не безопасно во время компиляции.

Я пришел к следующему , что неудивительно, что не компилируется:

public static async Task<BulkWriteResult<TDocument>> BulkUpsertAsync<TDocument, TField>(this IMongoCollection<TDocument> collection, List<TDocument> entries, Expression<Func<TDocument, TField>> filterField, Expression<Func<TDocument, TField>> valueField)
    {
        var bulkOps = new List<WriteModel<TDocument>>();

        foreach (var entry in entries)
        {
            var filter = Builders<TDocument>.Filter.Eq(filterField, valueField);

            var upsertOne = new ReplaceOneModel<TDocument>(filter, entry) { IsUpsert = true };

            bulkOps.Add(upsertOne);
        }

        return await collection.BulkWriteAsync(bulkOps);
    }

Я подозреваю, что тип valueField неверен, но дополнительно компилятор жалуется, что

Ошибка CS1503: Аргумент 1: невозможно преобразовать из 'System.Linq.Expressions.Expression<System.Func<TDocument, TField>>' до 'MongoDB.Driver.FieldDefinition<TDocument, System.Linq.Expressions.Expression<System.Func<TDocument, TField>>>' (21, 48)

Ответы [ 2 ]

1 голос
/ 13 марта 2020

Мне удалось заставить это работать, используя скомпилированную лямбду.

public static async Task<BulkWriteResult<TDocument>> BulkUpsertAsync<TDocument, TField>(
        this IMongoCollection<TDocument> collection,
        List<TDocument> entries,
        Expression<Func<TDocument, TField>> filterField)
    {
        var bulkOps = new List<WriteModel<TDocument>>();
        foreach (var entry in entries)
        {
            var filterFieldValue = filterField.Compile();
            var filter = Builders<TDocument>.Filter.Eq(filterField, filterFieldValue(entry));
            var upsertOne = new ReplaceOneModel<TDocument>(filter, entry) { IsUpsert = true };

            bulkOps.Add(upsertOne);
        }

        return await collection.BulkWriteAsync(bulkOps);
    }

Expression<Func<TDocument, TField>> в сигнатуре метода позволяет мне указывать на свойство или поле на TDocument, которое используется как в создание фильтра для MongoDB, а также в итераторе, который создает ReplaceOneModel для каждого entry in entries.

Его можно назвать следующим образом:

// Given some List<T> of entries to upsert...
List<SomePoco> SomePocos = GetListOfPocoFromSomewhere();
// This will match existing documents on a field called "Name".
await SomeMongoCollection.BulkUpsertAsync(SomePocos, filterField => filterField.Name);

Надеюсь, что это помогает кому-то!

0 голосов
/ 12 марта 2020

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

Мы не используем методы расширений, но у нас есть пакет-обертка-обертка, который мы используем для доступа к mon go через C # / Core 2.1

У нас есть специфика c интерфейс IMongoDocument, который мы затем можем использовать для определения наших фильтров и т. Д. c

public async Task UpdateAsync<T>(T entity) where T : IMongoDocument
{
    string originalDocumentVersion = entity.DocumentVersion;
    var filter = Builders<T>.Filter.Eq(x => x.Id, entity.Id);

    entity.DocumentVersion = ObjectId.GenerateNewId().ToString();

    ReplaceOneResult result = await this.Collection<T>().ReplaceOneAsync(this.session, filter, entity).ConfigureAwait(false);

    CheckResult(result.IsAcknowledged, result.IsAcknowledged ? result.ModifiedCount : 0);
}

public interface IMongoDocument : IDocumentIdentifier, IDocumentVersion
{     
}

public interface IDocumentIdentifier
{
    string Id { get; set; }
}

public interface IDocumentVersion
{
    string DocumentVersion { get; set; }
}

this.session используется, потому что мы используем транзакции mon go и его можно игнорировать, если вы не являются!

В этом примере показано обновление, но передача списка IMongoDocuments (или того, чем может оказаться ваш интерфейс) методу с ограничением generi c должна позволить вам быть обобщенным c (в точку) для вашего массового обновления. Кажется, ключевым моментом является правильное построение фильтра.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...