Когда я должен назвать SaveChanges () при создании 1000-х объектов Платформы Объекта? (как во время импорта)

Я выполняю импорт, который будет иметь 1000-е записей на каждом выполнении. Просто ища некоторое подтверждение на моих предположениях:

Какой из них имеет большую часть смысла:

  1. Выполненный SaveChanges() каждый AddToClassName() звонить.
  2. Выполненный SaveChanges() каждое n количество AddToClassName() вызовы.
  3. Выполненный SaveChanges() после всего из AddToClassName() вызовы.

Первая опция является, вероятно, медленным правом? Так как это должно будет проанализировать объекты EF в памяти, генерируйте SQL и т.д.

Я предполагаю, что второй вариант является наилучшим из обоих миров, так как мы можем обернуть выгоду попытки вокруг этого SaveChanges() звоните и только потеряйте n количество записей за один раз, если один из них перестал работать. Возможно, сохраните каждый пакет в Списке <>. Если SaveChanges() вызов успешно выполняется, избавьтесь от списка. Если это перестало работать, зарегистрируйте объекты.

Последняя опция, вероятно, закончила бы тем, что была очень медленной также, так как каждый объект EF должен будет быть в памяти до SaveChanges() назван. И если бы сохранение перестало работать, то ничто не фиксировалось бы, правильно?

73
задан John Bubriski 18 December 2009 в 22:14
поделиться

2 ответа

Я бы сначала протестировал его, чтобы убедиться. Производительность не должна быть такой плохой.

Если вам нужно ввести все строки в одной транзакции, вызовите ее после всего класса AddToClassName. Если строки можно вводить независимо, сохраняйте изменения после каждой строки. Согласованность базы данных важна.

Второй вариант мне не нравится. Меня бы сбило с толку (с точки зрения конечного пользователя), если бы я сделал импорт в систему, и он отклонил бы 10 строк из 1000 только потому, что 1 - это плохо. Вы можете попробовать импортировать 10, и если это не удастся, попробуйте один за другим, а затем войдите.

Проверьте, занимает ли это много времени. Не пишите «возможно». Вы еще этого не знаете. Только когда это действительно проблема, подумайте о другом решении (marc_s).

EDIT

Я провел несколько тестов (время в миллисекундах):

10000 строк:

SaveChanges () после 1 строки : 18510, г. 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
{
    //
    // GET: /Test/
    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);
    }
}
55
ответ дан 24 November 2019 в 12:24
поделиться

Если вам нужно импортировать тысячи записей, я бы использовал для этого что-то вроде SqlBulkCopy, а не Entity Framework.

12
ответ дан 24 November 2019 в 12:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: