2015-11-03 2 views
0

У меня довольно простое приложение WPF, которое использует Entity Framework. На главной странице приложения есть список записей, которые я получаю из базы данных при запуске.Как мне реагировать на приложение во время операций Entity Framework?

Каждая запись имеет изображение, поэтому операция может быть немного медленной, когда беспроводной сигнал плохой. Я хотел бы, чтобы это (и многие из моих операций с SQL) выполнялось в фоновом режиме, если это было возможно. У меня есть настройка async/await, и сначала она работает точно так, как мне хотелось, но теперь я вижу, что мое приложение становится невосприимчивым при доступе к БД.

В конце концов я думаю, что собираюсь загрузить текст в одном запросе и изображениях в другую фоновую операцию и загрузить их по мере их поступления. Таким образом, я сразу получаю важные вещи, и фотографии могут появиться в фоновом режиме, но, как все происходит, все еще похоже, что он закроется, если я это сделаю.

Кроме того, я пытаюсь реализовать что-то для решения проблем с подключением (в случае, если wifi отключается на мгновение), так что приложение уведомляет пользователя о проблеме подключения, автоматически повторяет несколько раз и т. Д. I поместите попытку catch для SQL-исключения, которая, похоже, работает на меня, но все приложение блокируется примерно на минуту, пока пытается подключиться к БД.

Я попытался проверить мой асинхронный/ожидающий, используя await Task.Delay(), и все очень отзывчивое, как ожидалось, в ожидании задержки, но все блокируется, ожидая .ToListAsync(). Это нормально и ожидается? Мое понимание async/await довольно ограничено.

Мой код в некотором роде беспорядочен (я новичок), но он делает то, что мне нужно для этого, по большей части. Я понимаю, что, вероятно, есть много улучшений, которые я могу сделать, и лучшие способы сделать что-то, но один шаг за один раз здесь. Моя главная цель прямо сейчас заключается в том, чтобы не допустить, чтобы приложение зависало во время доступа к исключениям в базе данных и чтобы пользователь не уведомлял о том, что делает приложение (поиск, попытка доступа к db, невозможность достичь БД и повторная попытка и т. Д.), А не замораживание , и это то, что они собираются думать, когда они видят, что он не отвечает за минуту.

Некоторые из моего кода:

В моей основной модели представления

DataHelper data = new DataHelper(); 
private async void GetQualityRegisterQueueAsync() 
    { 
     try 
     { 
      var task = data.GetQualityRegisterAsync(); 
      IsSearching = true; 
      await task; 
      IsSearching = false; 
      QualityRegisterItems = new ObservableCollection<QualityRegisterQueue>(task.Result); 
      OrderQualityRegisterItems(); 
     } 
     catch (M1Exception ex) 
     { 

      Debug.WriteLine(ex.Message); 
      Debug.WriteLine("QualityRegisterLogViewModel.GetQualityRegisterQueue() Operation Failed"); 
     } 

    } 

Мои данные Helper Класс

public class DataHelper 
{ 

    private bool debugging = false; 

    private const int MAX_RETRY = 2; 
    private const double LONG_WAIT_SECONDS = 5; 
    private const double SHORT_WAIT_SECONDS = 0.5; 
    private static readonly TimeSpan longWait = TimeSpan.FromSeconds(LONG_WAIT_SECONDS); 
    private static readonly TimeSpan shortWait = TimeSpan.FromSeconds(SHORT_WAIT_SECONDS); 
    private enum RetryableSqlErrors 
    { 
     ServerNotFound = -1, 
     Timeout = -2, 
     NoLock = 1204, 
     Deadlock = 1205, 
    } 

    public async Task<List<QualityRegisterQueue>> GetQualityRegisterAsync() 
    { 
     if(debugging) await Task.Delay(5000); 

     var retryCount = 0; 
     using (M1Context m1 = new M1Context()) 
     { 
      for (; ;) 
      { 
       try 
       { 
        return await (from a in m1.QualityRegisters 
           where (a.qanClosed == 0) 
           //orderby a.qanAssignedDate descending, a.qanOpenedDate 
           orderby a.qanAssignedDate.HasValue descending, a.qanAssignedDate, a.qanOpenedDate 
           select new QualityRegisterQueue 
           { 
            QualityRegisterID = a.qanQualityRegisterID, 
            JobID = a.qanJobID.Trim(), 
            JobAssemblyID = a.qanJobAssemblyID, 
            JobOperationID = a.qanJobOperationID, 
            PartID = a.qanPartID.Trim(), 
            PartRevisionID = a.qanPartRevisionID.Trim(), 
            PartShortDescription = a.qanPartShortDescription.Trim(), 
            OpenedByEmployeeID = a.qanOpenedByEmployeeID.Trim(), 
            OpenedByEmployeeName = a.OpenedEmployee.lmeEmployeeName.Trim(), 
            OpenedDate = a.qanOpenedDate, 
            PartImage = a.JobAssembly.ujmaPartImage, 
            AssignedDate = a.qanAssignedDate, 
            AssignedToEmployeeID = a.qanAssignedToEmployeeID.Trim(), 
            AssignedToEmployeeName = a.AssignedEmployee.lmeEmployeeName.Trim() 
           }).ToListAsync(); 
       } 
       catch (SqlException ex) 
       { 
        Debug.WriteLine("SQL Exception number = " + ex.Number); 
        if (!Enum.IsDefined(typeof(RetryableSqlErrors), ex.Number)) 
         throw new M1Exception(ex.Message, ex); 

        retryCount++; 

        if (retryCount > MAX_RETRY) throw new M1Exception(ex.Message, ex); ; 
        Debug.WriteLine("Retrying. Count = " + retryCount); 
        Thread.Sleep(ex.Number == (int)RetryableSqlErrors.Timeout ? 
                  longWait : shortWait); 
       } 
      } 
     } 


    } 


} 

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

ответ

0

Если вы вызываете этот метод из потока пользовательского интерфейса, вы будете перегружать захват контекста потока пользовательского интерфейса и обратно. Кроме того, ваша служба не обязательно будет «Performant», потому что она должна подождать, пока поток пользовательского интерфейса будет свободен, прежде чем он сможет продолжить.

Решение просто: просто вызовите метод, передающий параметр «false» ConfigureAwait, когда вы сделали вызов.

.ToListAsync().ConfigureAwaiter(false); 

Я надеюсь, что это помогает

+0

Я дал это попробовать в обоих моих ViewModel и моей DataHelper, но это, похоже, ничего не менять. – MEEECNC