понедельник, 19 августа 2013 г.

Database scan with Entity Framework (part 2 multithread).

В первой части я рассказал, как использовать EF 5.0 для задачи сканирования БД с целью выявления новых заданий. Однако в WPF такой подход в чистом виде применять нельзя из-за того, что задача сканирования базы данных должна постоянно выполняться в программе, а также она может занимать значительный промежуток времени. Это значит, что при синхронном ее выполнении в UI потоке, наше приложение практически постоянно будет находиться в режиме зависания, а это недопустимо. Решение этой задачи состоит в асинхронном выполнении задачи сканирования БД, с последующим выводом результатов в UI thread, так как только через UI thread мы можем менять состояние GUI. Программных реализаций такого решения много, тут я покажу использование асинхронной модели на основе задач (TAP) (.net framework 4.5) в сочетании с возможностями C# 5.0.
Асинхронный метод должен возвращать объект Task, Task<TResult>, или void. Хотя возвращать void рекомендуется только для обработчиков событий.
Перейдем непосредственно к программной реализации. Перед тем как запустить метод асинхронного сканирования БД, при первом запуске программы необходимо получить максимальный идентификатор записи в БД.

private void Application_Startup(object sender, StartupEventArgs e)
        {  
            var mainWindow = new MainWindow();
            Current.MainWindow = mainWindow;
            var viewModel = new MainViewModel(mainWindow);
            mainWindow.DataContext = viewModel;
            mainWindow.Tb = (TaskbarIcon)FindResource("NotifIcon");
            if (mainWindow.Tb != null) 
                mainWindow.Tb.DataContext = viewModel;
            //var trayToolTip = new UserControl1 {DataContext = viewModel};
            viewModel.GetId(); // получаем максимальный ID
            mainWindow.Show();
        }
public class MainViewModel : ViewModelBase
    {
      public void GetId()
             {
                  using (var context = new CCYCEntities())
                  {
                      LastEngineId = DataService.GetLastEngId(context).ToString(CultureInfo.InvariantCulture);
                  }
    }
class DataService
    {
        internal static long GetLastEngId(CCYCEntities context)
        {
            var tmp =
                (from eng in context.Engines orderby  eng.ID_Engine select eng.ID_Engine).ToList().LastOrDefault();
            return tmp;
        }
    }

После этого в классе DataService реализуем асинхронное сканирование БД:

class DataService
    {
        internal static Task'<'List'<'Engine'>''>' GetNewEnginesAsync( CCYCEntities context, long maxId, CancellationToken cancellationToken)
        {
            return Task.Run(() =>
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var tmp = (from d in context.Engines where d.ID_Engine > maxId select d).ToList();
                    return tmp;
                });
        }
}

Использование в MainViewModel:

private async void Test()
        {
            var ctn = new CancellationToken();
            using (var context = new CCYCEntities())
            {
  try
                {
                    while (true)
                    {
                        var rez = await DataService.GetNewEnginesAsync(context,_lastId,ctn);
                        await Task.Delay(3000);
                        TestAsync = rez.Count.ToString(CultureInfo.InvariantCulture);
                        _lastId +=  rez.Count();
                        LastEngineId = _lastId.ToString(CultureInfo.InvariantCulture);
                        var lastOrDefault = rez.LastOrDefault();
                        if (lastOrDefault != null)
                        {
                            var nameEng = lastOrDefault.NameEngine;
                            const string act = "was added";
                            Welcome = string.Format("{0} {1}", nameEng, act); 
                        }
                        else Welcome = "No new Engines";                    
                      }
Тут свойства LastEngineID, Welcome и void Test() связаны с соответствующими компонентами GUI WPF.

Полезные ссылки: 
1. MSDN
2. MSDN Magazine

Комментариев нет:

Отправить комментарий