2016-03-24 6 views
0

Я использую ASP.MVC 5 и EF6. Моя цель - заполнить базу данных данными с другого сервера, а не блокировать пользовательский интерфейс.ASP.MVC EF6 базовый интерфейс блокировки заполнения

Заполнение базы данных занимает много времени, около 10-15 минут. И пользователь может работать с системой в это время. Теперь он очень сильно замерзает в течение нескольких минут. Таким образом, факт состоит в том, что пользователь загружает данные из базы и заполняет задачу загрузки данных в базу данных за один раз.

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

Когда пользователь пользуется системой, ему всегда нужно загрузить некоторые данные из моей базы данных. Например: когда пользователь видит какую-то страницу в течение 1 минуты, есть 10000 загруженных данных, на 2 минуты больше данных и т. Д.

public ActionResult SomeAction() 
{ 
    new Thread(async() => 
    { 
     Thread.CurrentThread.IsBackground = true; 
     await ImportTask(); 
    }).Start(); 
    ... (some code) 
    return View(); 
} 

public async Task ImportTask() 
{ 
    for (var i = 0; i < 180; i++) 
    { 
     using (var context = new ApplicationDBContext()) 
     { 
      // synchronized data download with some sided library 
      var data = GetSomeDataFromAnotherServer(i); 
      foreach (var dataPart in data) 
      { 
       var stat = context.Data 
        .FirstOrDefault(x => 
         x.EnumProperty == dataPart.EnumProperty && 
         x.LongProperty == dataPart.LongProperty && 
         x.DateTimeProperty == dataPart.DateTimeProperty) 
        ?? new DataType() 
        { 
         EnumProperty = dataPart.EnumProperty, 
         LongProperty = dataPart.LongProperty, 
         DateTimeProperty == dataPart.DateTimeProperty 
        }; 
       ... (some another stat filling) 

       context.Statistics.AddOrUpdate(stat); 
      } 
      await context.SaveChangesAsync(); 
     } 
    } 
} 

И еще один вопрос: у меня есть возможность загружать данные примерно с 10 потоками за один раз. Как я могу изменить этот код, чтобы сделать это?

p.s. код может содержать некоторые опечатки здесь

ответ

0

Не используйте класс Thread, это старая аркана, трудно понять/использовать api, которая устарела для 99% случаев использования. Предпочитаете Task.Run для выполнения работы с ЦП, хотя не используйте это в asp.net. asp не очень хорошо работает с вами, когда вы крадете потоки из него, и легко создать взаимоблокировки.

Когда имеешь дело с истинным асинхронным API (как DbContext.SaveChangesAsync()), просто использовать async/await, не возиться с потоками на всех, они не дают никаких преимуществ по производительности в веб-контексте, и они даже повредить его (и больно масштабируемость тоже).

0

Try:

 public async Task<ActionResult> SomeAction() 
    { 
     await Task.Run(() => { ImportTask(); }); 
     return View(); 
    } 

    public async Task ImportTask() 
    { 
     using (var context = new ApplicationDBContext()) 
     { 
      context.Configuration.AutoDetectChangesEnabled = false; 
      for (var i = 0; i < 180; i++) 
      { 
       var data = GetSomeDataFromAnotherServer(i); 
       foreach (var dataPart in data) 
       { 
        var stat = context.Data.FirstOrDefault(x => x.EnumProperty == dataPart.EnumProperty && x.LongProperty == dataPart.LongProperty && x.DateTimeProperty == dataPart.DateTimeProperty) 
         ?? new DataType() 
                                                       { 
        ... 
        (some 
        another stat 
        filling) 

        context.Statistics.AddOrUpdate(stat); 
       } 
      } 
      await context.SaveChangesAsync(); 
     } 
    } 

Использование using (var context = new ApplicationDBContext()) внутри for (var i = 0; i < 180; i++) приводит 180 создать & Dispose операции DbContext. Также для объемных вставок, для ускорения можно использовать context.Configuration.AutoDetectChangesEnabled = false; или попробуйте эту библиотеку https://efbulkinsert.codeplex.com/

+0

Я не вижу способ использовать только 1 AppDBContext для всех операций. Проблема в том, что контекст может быть внезапно закрыт через 1-2 минуты с исключением. Мы избегаем этой проблемы с контекстом Owin, но она работает только для действий, а задача не является действием. – stationfuk

+0

Восстановить контекст после n вставок или использовать 'System.Data.SqlClient.SqlBulkCopy'. Исключение после 1-2 минут у вас есть, вероятно, «OutOfMemoryException». См. [Самый быстрый способ вставки в инфраструктуру Entity Framework] (http: // stackoverflow.com/a/5942176/5246410) –

+0

'SaveChanges' также должен быть внешним циклом, так что вы не получите вызов базы данных для каждого обновления элемента. –

0

Вам не нужно создавать сырые потоки или выполнять задачи через task.Run() в контексте ASP.NET, так как это в основном просто препятствие производительности в контексте ASP.NET. Я бы не использовал его для реализации «огня и забыть», так как он очень ненадежный. Если вы хотите пожара и забыть, вы можете посмотреть на использование: HostingEnvironment.QueueBackgroundWorkItem, но я не уверен, что это лучший подход.

Возможно, вы решили создать другой процесс для выполнения этого вместо того, чтобы это происходило во время веб-запроса? Возможно, служба, работающая на сервере, может отслеживать, когда кто-то входит в систему и осуществляет перемещение данных? Другой вариант может заключаться в том, что вы вызываете эту операцию перемещения данных из некоторого javascript в браузере, чтобы его отпустить?

Смежные вопросы