Как (DbContext) SaveChanges () можно использовать для большого количества записей?- решение для лучшей производительности нужно искать и находить - PullRequest
0 голосов
/ 28 ноября 2018

Может быть запущен SaveChanges () для большого количества записей, но производительность не приемлема.

Вот пример. Две таблицы:

[contact] (id, name,zip, city, status)

[zip_city] (id, zip, city)

Требуется обновить статус в таблице contact для всех столбцов zip, город в таблице zip_city

Это работает, и производительность приемлема, когда число записей меньше 10 000.

Но для +10 000 записей Visual Studio (отладчик) жалуется, что это занимает слишком много времени: CLRне удалось перейти из контекста COM 0xb67898 в контекст COM 0xb67728 в течение 60 секунд .... при построении запроса "qry"

var ctx = new DbContext();

var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z })
           .Select(x => new dtoContact { id = x.c.id }).ToList();

foreach (var con in ctx.contact)
{
    if (qry.Any(x => x.Id == con.id))
    {
        con.status = "P/O";
    }
    else
    {
        con.status = "???";
    }
}

ctx.SaveChanges();

С помощью следующего кода он вызывает то же сообщение, выполнив ctx.SaveChanges ();

var ctx = new DbContext();

var zc = ctx.zip_city.ToList();

foreach (var con in ctx.contact)
{
    if (zc.Any(x => x.zip == con.zip && x.city == con.city))
    {
        con.status = "P/O";
    }
    else
    {
        con.status = "???";
    }
}

ctx.SaveChanges();

Ссылка: объект передачи данных

public class dtoContact 
{
    public int id { get; set; }
    public string name { get; set; }
    public string zip { get; set; }
    public string city { get; set; }
    public string status { get; set; }
}

Информация: сообщение Visual Studio (отладчик)! Перевод Google с немецкой версии VS15

Помощник по управляемой отладке "ContextSwitchDeadlock" обнаружил проблему в C: \ Projects \ Sweepstakes_EF6 \ TrafficSightAct \ bin \ Debug \ Sweepstakes.exe ".Дополнительная информация: CLR не удалось перейти из контекста COM 0xb67898 в контекст COM 0xb67728 в течение 60 секунд.Поток, которому принадлежит целевой контекст / квартира, либо ожидает без перемещения сообщений, либо обрабатывает очень длительные операции без перемещения сообщений Windows.Такая ситуация обычно снижает производительность и может даже привести к тому, что приложение перестает отвечать на запросы или увеличивает использование памяти.

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

Спасибо всем за ценные комментарии!

Чтобы оставить вышеупомянутые вопросы-комментарии в строках, я создаю ответ здесь.

Я протестировал в комментариях предоставленные рекомендации с 27 000 записей в контакте таблицы и 47 000 записей в таблице zip_city, но без изменения индекса базы данных (добавьте индекс, предложенный Panagiotis Kanavos).

Ниже приведены результаты теста производительности

Исходный код 1, требуется 113 секунд .

var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z })
           .Select(x => new dtoContact { id = x.c.id }).ToList();
foreach (var con in ctx.contact)
{
    if (qry.Any(x => x.Id == con.id))
    {
        con.status = "P/O";
    }
    else
    {
        con.status = "???";
    }
}
ctx.SaveChanges();

Основываясь на DevilSuichiro, «массовые сохранения» недоступны в DbContext с MySQL, поэтому здесь только SaveChanges() вызывается для отдельных партий из 100 объектов.Это займет 107 секунд .

var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z })
           .Select(x => new dtoContact { id = x.c.id }).ToList();
int count = 0;
foreach (var con in ctx.contact)
{
    if (qry.Any(x => x.Id == con.id))
    {
        con.status = "P/O";
    }
    else
    {
        con.status = "???";
    }
    ++count;
    if (count % 100 == 0)
    {
        ctx.SaveChanges();
    }
}
ctx.SaveChanges();

На основе рандом-рандома, HashSet используется для повышения производительности.Это по-прежнему занимает более 100 секунд .

var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z }).Select(id = x.c.id);
var ids = new HashSet<int>(qry);           
foreach (var con in ctx.contact)
{
    if (ids.Contains(con.id))
    {
        con.status = "P/O";
    }
    else
    {
        con.status = "???";
    }
}
ctx.SaveChanges();

Исходный код 2, требуется 67 секунд .

var ctx = new DbContext();
var zc = ctx.zip_city.ToList();
foreach (var con in ctx.contact)
{
    if (zc.Any(x => x.zip == con.zip && x.city == con.city))
    {
        con.status = "P/O";
    }
    else
    {
        con.status = "???";
    }
}
ctx.SaveChanges();

По материалам PanagiotisKanavos, использование любых ORM (как здесь DbContext SaveChanges ()) для этого Data-UPDATE действительно не подходит.

Я тестировал код от alex, nvoigt, это занимает менее 2 секунд .

var ctx = new DbContext();
string sql = "update contact set status = '???'; " + 
    "update contact c inner join zip_city z on c.plz = z.plz and c.ort = z.ort set c.status = 'P/O'; ";
ctx.Database.ExecuteSqlCommand(sql);
0 голосов
/ 28 ноября 2018

В этом случае я бы рекомендовал использовать SqlCommand.

например:

var ctx = new DbContext();

string sql = "update contact c inner join zip_city z " +
             "on c.plz = z.plz and c.ort = z.ort set c.status = 'P/O'; ";
sql = sql  + "update contact set status = '???' where status <> 'P/O'; ";

ctx.Database.ExecuteSqlCommand(sql);

Я думаю, это может занять всего несколько секунд для ваших 27 000 записей.

Пожалуйста, попробуйте.

...