Копирование данных из запроса LiteDb в список вызывает исключение System.InvalidOperationException - PullRequest
0 голосов
/ 20 декабря 2018

Я играл с LiteDb, чтобы улучшить производительность моего фида данных.Фид работает в базе данных Azure, и чтобы уменьшить количество используемых DTU и соединений (я делаю некоторую логику в параллельном цикле поиска), я извлекаю данные из базы данных, помещаю их в индекс LiteDb, а затем запускаюмоя логика вокруг этого.Большинство моих сценариев работают отлично.Сначала я делаю массовое добавление индексов, а затем заново открываю litdb в режиме только для чтения и выполняю свои запросы. Примерно так:

using (var db = new LiteDatabase("SiloLite.db"))
                {
                    db.DropCollection("SiloProduct");
                    var products = db.GetCollection<SiloProduct>("SiloProduct");
                    products.InsertBulk(_siloproductRepository.Table.ToList());
                    products.EnsureIndex(x => x.Sku);
                    db.Engine.EnsureIndex("SiloProduct", "UniqueProdId", "$.Sku+';'+$.ParentProductId", true);
                }
using (var db = new LiteDatabase($"Filename={AppDomain.CurrentDomain.BaseDirectory}\\SiloLite.db;mode=ReadOnly"))
                {
                    var products = db.GetCollection<SiloProduct>("SiloProduct");
                    var manufacturers = db.GetCollection<SiloManufacturer>("SiloManufacturer");
                    var productspecificationmap =
                        db.GetCollection<SiloProductSpecificationAttributeMapping>(
                            "SiloProductSpecificationAttributeMapping");
                    var specificationAttributes =
                        db.GetCollection<SiloSpecificationAttribute>("SiloSpecificationAttribute");
                    var specificationAttributeOptions =
                        db.GetCollection<SiloSpecificationAttributeOption>("SiloSpecificationAttributeOption");

                    Parallel.ForEach(katartlist, KatartItem =>
                    {
                        SaveData(KatartItem, specificationAttributes, specificationAttributeOptions, productspecificationmap,
                            manufacturers, products);
                    });
                    _logger.Information("Completed updating Product Specifications");
                }

И он прекрасно работает во всех сценариях, кроме одного.Если я попытаюсь выполнить запрос, а затем попытаться сохранить эти данные в другой коллекции LiteDb жалуется.Это то, что я пытаюсь сделать

var currentspecOptCollection = new List<SiloProductSpecificationAttributeMapping>(liteProdSpecCollection.Find(x => x.ProductId.Equals(productId)).ToList());
                    var specstoDelete = currentspecOptCollection
                                                          .Where(x => !specificationattributeoptionlist.Contains(x.SpecificationAttributeOptionId))
                                                          .ToList();
                    if (specstoDelete.Any())
                    {
                        foreach (var specattr in specstoDelete)
                        {
                            _specattrDeleteList.Add(specattr);
                        }

                    }

Эта логика выполняется внутри Parallel.Foreach и LiteDb выдает эту ошибку

System.InvalidOperationException: Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.
   at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
   at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
   at LiteDB.CacheService.GetPage(UInt32 pageID)
   at LiteDB.PageService.GetPage[T](UInt32 pageID)
   at LiteDB.QueryCursor.Fetch(TransactionService trans, DataService data, BsonReader bsonReader)
   at LiteDB.LiteEngine.Find(String collection, Query query, Int32 skip, Int32 limit)+MoveNext()
   at LiteDB.LiteEngine.Find(String collection, Query query, String[] includes, Int32 skip, Int32 limit)+MoveNext()
   at LiteDB.LiteCollection`1.Find(Query query, Int32 skip, Int32 limit)+MoveNext()
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found)
   at CreateOrUpdateProductSpecifications(KatartItem katartItem, VarData vd, SiloManufacturer manufacturer, SiloProduct product, LiteCollection`1 liteSpecCollection, LiteCollection`1 liteSpecOptCollection, LiteCollection`1 liteProdSpecCollection) in *** 1399

Не уверен, что я делаю здесь неправильно. Любые указатели помогут

1 Ответ

0 голосов
/ 05 августа 2019

Мне пришлось решить аналогичную проблему, в которой я читал данные из LiteDB и затем помещал данные в другой список.Я решил это, написав свое параллельное выполнение следующим образом:

public static async Task LoadDataFromDB()
{
    var tasks = new List<Task>();

    using (var db = new LiteDatabase(fullDbPath))
    {
        var bookChapterCollection = db.GetCollection<Chapter>("bookData");
        foreach (var volume in Volume.ListOfVolumes)
        {
            tasks.Add(Task.Factory.StartNew(() =>
            {
                var volumeChapterFromDB = bookChapterCollection.Find(x => x.chapterVolume == volume.volume).toList();
                return volumeChapterFromDB;
            }
            ,TaskCreationOptions.LongRunning)
           .ContinueWith(task => {
                BookChaptersBag.Add(task.Result);
                return task.Result;
            })
            );
        }
        await Task.WhenAll(tasks).ContinueWith(task =>
        {
            BookChaptersBag.CompleteAdding();
        });
    }
}

Здесь BookChaptersBag - это BlockingCollection<List<Chapter>>, представляющий собой потокобезопасную коллекцию из пространства имен System.Collections.Concurrent.После написания моего параллельного выполнения, как описано выше, я перестал получать исключение.

Надеюсь, это поможет!

...