У меня есть веб-метод, который вставляет кучу рецептов в очередь в базе данных (для хранения рецептов, которые пользователь заинтересован в приготовлении, аналогично очереди фильмов в 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) Сначала загрузите всю очередь пользователя из базы данных и проверьте
этот список первым. Если рецепт уже существует, просто
continue
в
цикл for. Плюсы: никакие ненужные вставки SQL не отправляются на
база данных. Минусы: медленнее, особенно если у пользователя большая очередь.
- 2) Не используйте ActiveRecord и передайте весь массив recipeIds
в функцию SQL. Эта функция проверит, существует ли каждая строка
первый. Плюсы: Потенциально быстро, позволяет SQL обрабатывать всю грязную работу.
Минусы: ломает шаблон ActiveRecord и требует нового кода БД, который
часто сложнее поддерживать и дороже в реализации.
- 3) CreateAndFlush после каждого цикла. По сути, не запускайте все это
цикл в одной транзакции. Зафиксируйте каждый ряд, как он добавлен и
ловить ошибки SQL и игнорировать. Плюсы: низкая стоимость запуска и не
требуется новый код SQL сервера. Минусы: потенциально медленнее для вставки
много строк в базе данных одновременно, хотя это сомнительно для пользователя
когда-нибудь подаст более десятка новых рецептов одновременно.
Есть ли другие маленькие хитрости с Каслом или фреймворком NHibernate? Кроме того, моим SQL-сервером является PostgreSQL 9.0. Спасибо!
Обновление:
Я сделал снимок при первом заходе на посадку, и, похоже, он работает довольно хорошо. Мне пришло в голову, что мне не нужно загружать всю очередь, только те, которые появляются в recipeIds. Я считаю, что мой цикл foreach()
теперь равен O (n ^ 2) в зависимости от эффективности List<Guid>::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();
}
}