получив список объектов, используя C #, отправьте их в ravendb, не зная, какие из них уже существуют

Дано 1000 документов со сложной структурой данных. например класс Car, который имеет три свойства: Make и Model и одно свойство Id.

Каков наиболее эффективный способ в С# отправить эти документы в базу данных ворона (желательно в пакетном режиме) без необходимости индивидуально запрашивать коллекцию ворона, чтобы найти, что обновить, а что вставить. На данный момент я должен идти так. Что совершенно неэффективно. Примечание: _session — это оболочка IDocumentSession, где Commit вызывает SaveChanges, а Add вызывает Store.

    private void PublishSalesToRaven(IEnumerable<Sale> sales)
    {
        var page = 0;
        const int total = 30;
        do
        {
            var paged = sales.Skip(page*total).Take(total);
            if (!paged.Any()) return;
            foreach (var sale in paged)
            {
                var current = sale;
                var existing = _session.Query<Sale>().FirstOrDefault(s => s.Id == current.Id);
                if (existing != null)
                    existing = current;
                else
                    _session.Add(current);
            }
            _session.Commit();
            page++;
        } while (true);
    }

person afif    schedule 19.10.2011    source источник


Ответы (3)


Ваш пример кода вообще не работает. Основная проблема заключается в том, что вы не можете просто отключить ссылки и ожидать, что RavenDB распознает это:

if (existing != null)
    existing = current;

Вместо этого вам нужно обновлять каждое свойство по одному:

existing.Model = current.Model;
existing.Make = current.Model;

Таким образом вы можете облегчить отслеживание изменений в RavenDB и многих других фреймворках (например, NHibernate). Если вы хотите избежать написания этого неинтересного фрагмента кода, я рекомендую использовать AutoMapper:

existing = Mapper.Map<Sale>(current, existing);

Другая проблема с вашим кодом заключается в том, что вы используете Session.Query вместо Session.Load. Помните: если вы запрашиваете документ по его идентификатору, вам всегда нужно будет использовать Load! Основное отличие состоит в том, что один использует локальный кеш, а другой нет (то же самое относится к эквивалентным методам NHibernate ).

Хорошо, теперь я могу ответить на ваш вопрос: если я правильно вас понимаю, вы хотите сохранить кучу экземпляров Sale в своей базе данных, в то время как их следует либо добавить, если они не существовали, либо обновить, если они существовали. Правильно? Один из способов — исправить пример кода с помощью приведенных выше советов и позволить ему работать. Однако это приведет к одному ненужному запросу (Session.Load(existingId)) для каждой итерации. Вы можете легко избежать этого, если настроите индекс, который выбирает все идентификаторы всех документов в вашей коллекции продаж. Прежде чем вы перейдете к своим элементам, вы можете загрузить все существующие идентификаторы.

Тем не менее, я хотел бы знать, что вы на самом деле хотите сделать. Каков ваш домен/вариант использования?

person Daniel Lang    schedule 19.10.2011
comment
Спасибо Даниил за эти указатели. Очень полезно. Мой вариант использования — периодически запускать процесс, который опрашивает таблицу SQL-сервера и отправляет обновления/вставки в Raven. У меня уже есть механизм, который идентифицирует измененные/новые записи в таблице сервера sql с момента последнего обновления Raven (через свойство даты и времени LastModified в классе Car). - person afif; 20.10.2011
comment
Просто имейте в виду, что полностью полагаться на поля даты несколько опасно, когда речь идет о синхронизации между двумя базами данных на разных серверах, потому что системное время может измениться на одном из серверов... Кстати, если вы нашли мой ответ полезным, вы также может пометить это как ответ. - person Daniel Lang; 20.10.2011

Код вашего сеанса, похоже, не отслеживается API-интерфейсом RavenDB (у нас нет Add или Commit). Вот как вы это делаете в RavenDB

private void PublishSalesToRaven(IEnumerable<Sale> sales)
{
    sales.ForEach(session.Store);
    session.SaveChanges();
}
person Ayende Rahien    schedule 19.10.2011
comment
сеанс, который вы видите, является оболочкой IDocuentSession от Raven. Добавление вызывает метод Store. Фиксация вызовов SaveChanges. Что происходит с вашим кодом, когда я делаю session.Store для документа, который уже существует в Raven? будет ли Raven умным, чтобы понять, что его нужно обновить, а не добавить? - person afif; 19.10.2011
comment
FWIW помните, что ForEach - это нестандартный метод linq, вам нужен либо код, который реализует вас, либо какая-то встроенная библиотека, либо используйте стандартный блок foreach. - person Chris Marisic; 19.10.2011
comment
@afif - правильно ли это => если у вас есть продажа новой машины, идентификатор == null? В противном случае Id - это какое-то число? - person Pure.Krome; 20.10.2011
comment
@ Pure.Krome, на самом деле нет. Поскольку Машины поступают из существующего магазина, у них всегда есть идентификатор. И я использую тот же идентификатор, чтобы сохранить их для Raven. - person afif; 20.10.2011

Это то, что работает для меня прямо сейчас. Примечание. Метод InjectFrom взят из Omu.ValueInjecter (пакет nuget).

    private void PublishSalesToRaven(IEnumerable<Sale> sales)
    {
        var ids = sales.Select(i => i.Id);
        var existingSales = _ravenSession.Load<Sale>(ids);
        existingSales.ForEach(s => s.InjectFrom(sales.Single(i => i.Id == s.Id)));

        var existingIds = existingSales.Select(i => i.Id);
        var nonExistingSales = sales.Where(i => !existingIds.Any(x => x == i.Id));
        nonExistingSales.ForEach(i => _ravenSession.Store(i));

        _ravenSession.SaveChanges();
    }
person afif    schedule 20.10.2011