Я бы сначала проверил это, чтобы убедиться. Производительность не должна быть такой уж плохой.
Если вам нужно ввести все строки в одной транзакции, вызовите ее после всего класса AddToClassName. Если строки можно вводить независимо, сохраняйте изменения после каждой строки. Согласованность базы данных важна.
Второй вариант мне не нравится. Меня бы сбило с толку (с точки зрения конечного пользователя), если бы я сделал импорт в систему, и он отклонил бы 10 строк из 1000 только потому, что 1 - это плохо. Вы можете попробовать импортировать 10, и если это не удастся, попробуйте один за другим, а затем войдите.
Протестируйте, если это займет много времени. Не пишите «возможно». Вы еще этого не знаете. Только когда это действительно проблема, подумайте о другом решении (marc_s).
РЕДАКТИРОВАТЬ
Я провел несколько тестов (время в миллисекундах):
10000 рядов:
SaveChanges () после 1 строки: 18510,534
SaveChanges () после 100 строк: 4350,3075
SaveChanges () после 10000 строк: 5233,0635
50000 рядов:
SaveChanges () после 1 строки: 78496 929
SaveChanges () после 500 строк: 22302,2835
SaveChanges () после 50000 строк: 24022,8765
Так что на самом деле совершить фиксацию после n строк быстрее, чем после всего.
Моя рекомендация:
- SaveChanges () после n строк.
- Если одна фиксация не удалась, попробуйте ее одну за другой, чтобы найти неисправную строку.
Тестовые классы:
СТОЛ:
CREATE TABLE [dbo].[TestTable](
[ID] [int] IDENTITY(1,1) NOT NULL,
[SomeInt] [int] NOT NULL,
[SomeVarchar] [varchar](100) NOT NULL,
[SomeOtherVarchar] [varchar](50) NOT NULL,
[SomeOtherInt] [int] NULL,
CONSTRAINT [PkTestTable] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Класс:
public class TestController : Controller
{
private readonly Random _rng = new Random();
private const string _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private string RandomString(int size)
{
var randomSize = _rng.Next(size);
char[] buffer = new char[randomSize];
for (int i = 0; i < randomSize; i++)
{
buffer[i] = _chars[_rng.Next(_chars.Length)];
}
return new string(buffer);
}
public ActionResult EFPerformance()
{
string result = "";
TruncateTable();
result = result + "SaveChanges() after 1 row:" + EFPerformanceTest(10000, 1).TotalMilliseconds + "<br/>";
TruncateTable();
result = result + "SaveChanges() after 100 rows:" + EFPerformanceTest(10000, 100).TotalMilliseconds + "<br/>";
TruncateTable();
result = result + "SaveChanges() after 10000 rows:" + EFPerformanceTest(10000, 10000).TotalMilliseconds + "<br/>";
TruncateTable();
result = result + "SaveChanges() after 1 row:" + EFPerformanceTest(50000, 1).TotalMilliseconds + "<br/>";
TruncateTable();
result = result + "SaveChanges() after 500 rows:" + EFPerformanceTest(50000, 500).TotalMilliseconds + "<br/>";
TruncateTable();
result = result + "SaveChanges() after 50000 rows:" + EFPerformanceTest(50000, 50000).TotalMilliseconds + "<br/>";
TruncateTable();
return Content(result);
}
private void TruncateTable()
{
using (var context = new CamelTrapEntities())
{
var connection = ((EntityConnection)context.Connection).StoreConnection;
connection.Open();
var command = connection.CreateCommand();
command.CommandText = @"TRUNCATE TABLE TestTable";
command.ExecuteNonQuery();
}
}
private TimeSpan EFPerformanceTest(int noOfRows, int commitAfterRows)
{
var startDate = DateTime.Now;
using (var context = new CamelTrapEntities())
{
for (int i = 1; i <= noOfRows; ++i)
{
var testItem = new TestTable();
testItem.SomeVarchar = RandomString(100);
testItem.SomeOtherVarchar = RandomString(50);
testItem.SomeInt = _rng.Next(10000);
testItem.SomeOtherInt = _rng.Next(200000);
context.AddToTestTable(testItem);
if (i % commitAfterRows == 0) context.SaveChanges();
}
}
var endDate = DateTime.Now;
return endDate.Subtract(startDate);
}
}
Я просто оптимизировал очень похожую проблему в своем собственном коде и хотел бы указать на оптимизацию, которая сработала для меня.
Я обнаружил, что большая часть времени при обработке SaveChanges, будь то обработка 100 или 1000 записей одновременно, связана с процессором. Таким образом, обрабатывая контексты с помощью шаблона производитель / потребитель (реализованного с помощью BlockingCollection), я смог гораздо лучше использовать ядра ЦП и получил от 4000 изменений в секунду (как сообщает возвращаемое значение SaveChanges) до более 14 000 изменений в секунду. Загрузка ЦП увеличилась с 13% (у меня 8 ядер) до 60%. Даже при использовании нескольких потребительских потоков я почти не облагал налогом (очень быструю) дисковую систему ввода-вывода, а загрузка ЦП SQL Server не превышала 15%.
Разгрузив сохранение на несколько потоков, у вас есть возможность настроить как количество записей до фиксации, так и количество потоков, выполняющих операции фиксации.
Я обнаружил, что создание 1 потока-производителя и (# ядер ЦП) -1 потребительских потоков позволило мне настроить количество записей, фиксируемых на пакет, так, чтобы количество элементов в BlockingCollection колебалось от 0 до 1 (после того, как поток потребителя взял один вещь). Таким образом, потребляющих потоков было достаточно работы для оптимальной работы.
Этот сценарий, конечно, требует создания нового контекста для каждого пакета, который, как я считаю, работает быстрее даже в однопоточном сценарии для моего варианта использования.
источник
Если вам нужно импортировать тысячи записей, я бы использовал что-то вроде SqlBulkCopy, а не Entity Framework для этого.
источник
Используйте хранимую процедуру.
Я считаю, что это будет самый простой и быстрый способ сделать это.
источник
Извините, я знаю, что эта ветка устарела, но я думаю, что это могло бы помочь другим людям с этой проблемой.
У меня была такая же проблема, но есть возможность проверить изменения перед их фиксацией. Мой код выглядит так, и он работает нормально. С помощью
chUser.LastUpdated
я проверяю, новая ли это запись или только изменение. Потому что невозможно перезагрузить запись, которой еще нет в базе данных.// Validate Changes var invalidChanges = _userDatabase.GetValidationErrors(); foreach (var ch in invalidChanges) { // Delete invalid User or Change var chUser = (db_User) ch.Entry.Entity; if (chUser.LastUpdated == null) { // Invalid, new User _userDatabase.db_User.Remove(chUser); Console.WriteLine("!Failed to create User: " + chUser.ContactUniqKey); } else { // Invalid Change of an Entry _userDatabase.Entry(chUser).Reload(); Console.WriteLine("!Failed to update User: " + chUser.ContactUniqKey); } } _userDatabase.SaveChanges();
источник
saveChanges()
удалить те, которые могут вызвать ошибку.SaveChanges
вызов. Вы не решаете эту проблему. Обратите внимание, что существует больше потенциальных причин сбоя SaveChanges, чем ошибок проверки. Кстати, вы также можете просто пометить объекты какUnchanged
вместо того, чтобы перезагружать / удалять их.SaveChanges
неудач. И это решает проблему. Если этот пост действительно беспокоит вас в этой теме, я могу удалить его, моя проблема решена, я просто пытаюсь помочь другим.GetValidationErrors()
, он «фальсифицирует» вызов базы данных и извлекает ошибки или что? Спасибо за ответ :)