Рекомендуемый подход для вставки большого количества строк с помощью Castle ActiveRecord и игнорирования любых дублирований

У меня есть веб-метод, который вставляет кучу рецептов в очередь в базе данных (для хранения рецептов, в которых пользователь заинтересован в приготовлении пищи, аналогично очереди фильмов NetFlix). Пользователь может отметить сразу несколько рецептов и поставить их в очередь. У меня есть код, похожий на этот:

[WebMethod]
public void EnqueueRecipes(SecurityCredentials credentials, Guid[] recipeIds)
{
    DB.User user = new DB.User(credentials);

    using (new TransactionScope(OnDispose.Commit))
    {
       foreach (Guid rid in recipeIds)
       {
          DB.QueuedRecipe qr = new DB.QueuedRecipe(Guid.NewGuid(), user, new DB.Recipe(rid));
          qr.Create();
       }
    }
}

У меня есть уникальное ограничение на UserId / RecipeId, поэтому пользователь может поставить рецепт в очередь только один раз. Однако, если они выбирают рецепт, который уже находится в их очереди, я действительно не хочу беспокоить пользователя сообщением об ошибке, я просто хочу проигнорировать этот рецепт.

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

  • 1) Сначала загрузите всю очередь пользователя из базы данных и сначала проверьте этот список. Если рецепт уже существует, просто продолжите в цикле for. Плюсы: в базу данных не отправляются ненужные вставки SQL. Минусы: Медленнее, особенно если у пользователя большая очередь.
  • 2) Не используйте ActiveRecord, а вместо этого передайте весь массив recipeIds в функцию SQL. Эта функция сначала проверяет, существует ли каждая строка . Плюсы: потенциально быстрый, позволяет SQL выполнять всю грязную работу. Минусы: нарушает шаблон ActiveRecord и требует нового кода БД, который часто сложнее поддерживать и дороже внедрять.
  • 3) CreateAndFlush после каждого цикла. По сути, не запускайте весь этот цикл в одной транзакции. Зафиксируйте каждую строку по мере ее добавления и перехватите ошибки SQL и проигнорируйте. Плюсы: низкая стоимость запуска и не требуется новый внутренний код SQL. Минусы: Потенциально медленнее при одновременной вставке большого количества строк в базу данных, хотя сомнительно, что пользователь когда-либо отправит более дюжины или около того новых рецептов одновременно.

Есть ли какие-нибудь другие хитрости с Castle или фреймворком NHibernate? Кроме того, мой SQL-сервер - PostgreSQL 9.0. Спасибо!

Обновление:

Я попытался использовать первый подход, и, похоже, он работает очень хорошо. Мне пришло в голову, что мне не нужно загружать всю очередь, только те, которые указаны в recipeIds. Я считаю, что мой цикл foreach () теперь O (n ^ 2) в зависимости от эффективности List :: Contains () , но я думаю, что это, вероятно, подходит для размеры, с которыми я буду работать.

//Check for dupes
DB.QueuedRecipe[] dbRecipes = DB.QueuedRecipe.FindAll(Expression.In("Recipe",
   (from r in recipeIds select new DB.Recipe(r)).ToArray()
));

List<Guid> existing = (from r in dbRecipes select r.Recipe.RecipeId).ToList();

using (new TransactionScope(OnDispose.Commit))
{
   foreach (Guid rid in recipeIds)
   {
      if (existing.Contains(rid))
         continue;

      DB.QueuedRecipe qr = new DB.QueuedRecipe(Guid.NewGuid(), user, new DB.Recipe(rid));
      qr.Create();
   }
}
7
задан Mike Christensen 1 July 2012 в 17:21
поделиться