среда, 23 июля 2014 г.

Глобальная view model для представлений asp.net mvc

Прежде чем рассказывать о реализации глобальной view model в представлениях, поясню, зачем это нужно. При использовании технологии asp.net mvc для создания web-приложений существует возможность представлению (содержащему привычную для нас html – разметку) передать класс - модели, который генерируется в контроллере, и тогда это представление будет называться строготипизированным. Однако бывают случаи, когда в представление необходимо передать несколько моделей, что опять же, является тривиальной задачей (см. ViewData или PartialView). Но иногда бывают случаи, когда необходимо сделать одну общую модель для нескольких представлений, при этом они не должны перестать быть строготипизированными. Приведу пример из реального проекта, где это понадобилось. Итак, необходимо организовать проведение баллистических расчетов для различных космических аппаратов, которые находятся на обслуживании и выполняют различные задачи (рис.1).

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

вторник, 15 апреля 2014 г.

Использование Google test framework для тестирования кода Visual C++ (Visual Studio 2012)

После прочтения книг по TDD и внедрения этой методики в мои проекты на C#, я решил разобраться как с этим дела обстоят в Visual С++. Тем более, что для тестирования математических расчетов я пользовался ручкой и бумагой, что было очень утомительно и трудозатратно. Посмотрев материалы по тестированию кода на MSDN, мой выбор пал на Google test framework из-за нескольких причин:  
  • простота установки;
  • интуитивно понятный язык написания тестов.
Так как для проекта мне нужно было тестировать только математические вычисления (не нужно было делать mock объекты и т.д.) функционал Google test меня полностью устраивал. Итак, установка:
компилируем google test в статическую библиотеку, для этого добавляем в наше решение с тестируемым проектом новый проект:

четверг, 10 апреля 2014 г.

Restful API

Точное определение restful web service можно найти по первой ссылке в Google, тут я расскажу про задачи, которые могут быть решены с использованием таких сервисов, а также покажу реализацию 4-х методов, которые должны быть в restful  сервисах (Get, Post, Put, Delete).
Итак, задача: у нас есть web приложение, которое отвечает за управление группировкой космических аппаратов, но кроме этого приложения существуют еще сторонние программы, которым необходим доступ к начальным условиям. Для решения этой задачи был сделан api controller, который реализовывал все необходимые операции. Контроллер возвращает либо JSON либо XML, в зависимости от заголовка запроса, но на реализации это никак не сказывается.

пятница, 27 декабря 2013 г.

Unit testing and Dependency Injection (DI) pattern.

Как известно использование сильно связанных классов это плохо, так как попытка внести изменения в один класс, неизбежно повлекут за собой, целую цепочку изменений в других классах. Особенно это проявляется при поддержке больших систем, состоящих из огромного числа классов. Кроме того, такой подход увеличивает сложность тестирования, так как один класс невозможно протестировать отдельно от другого. Для уменьшения связанности классов необходимо программировать на уровне интерфейсов, а не на уровне реализаций, т.е. всю логику, которая гипотетически может измениться необходимо, по возможности,  выносить в интерфейс. Затем, при создании  класса, в его конструктор можно передать объект, реализующий необходимый интерфейс и им инициализировать объект внутри класса (пример). Использование такого подхода описывает паттерн Dependency Injection.  Многие проблемы, возникающие при развитии  системы, могут быть решены,  если при программировании логики работы стараться придерживаться принципов проектирования классов (S.O.L.I.D).  При переходе к программированию на уровне интерфейсов  создание объектов может сильно усложниться, так как каждый раз при создании нового объекта нам необходимо будет настраивать  его зависимости.  Для простых объектов это может показаться не очень сложным, однако при усложнении системы простая инициализация объекта будет вести за собой целую цепочку вызовов разрешений зависимостей, и это будет необходимо делать при каждой инициализации объекта.  Для решения этой проблемы существует множество DI frameworks, которые при правильной настройке позволяют легко инициализировать объекты вместе с их зависимостями. В своей работе я использовал два framework-а: Unity и Ninject. Скажу честно, какой-то принципиальной разницы между ними не нашел, кроме того, что Ninject интегрируется с MVC «из коробки» и еще мелких различий, поэтому тут опишу только Ninject.

четверг, 26 декабря 2013 г.

Использование паттернов Unit of work и Generic Repository c Entity Framework 5.0

Использование паттернов Unit of work и Generic Repository c Entity Framework 5.0
Паттерн Unit of Work, по сути, нужен для консолидации всех методов, которые работают с базой данных. С его помощью можно улучшить контроль над изменением данных в БД, а также оптимизировать использование БД при обработке нескольких запросов одновременно. Использование Generic Repository упростит работу с EF Entities.
Создаем интерфейс IRepository, который описывает все необходимые операции с БД:
public interface IRepository<TEntity>  where TEntity : class
    {
        IEnumerable<TEntity> Get();
        TEntity GetById(object id);
        void Insert(TEntity entity);
        void Delete(object id);
        void Delete(TEntity entityToDelete);
        void Update(TEntity entityToUpdate);
    }

среда, 21 августа 2013 г.

Data binding WPF

   В данной статье я хотел бы рассказать о механизмах связывания данных, которые я использовал для отображения новых задач, поступающих в базу данных. 
  Об основах привязки данных в WPF можно прочитать по ссылкам в конце поста. Тут же я опишу только то, что мне понадобилось реальном проекте. Итак, исходные данные:
  • коллекция объектов, полученная из базы данных (List<Operations>)
  • TaskbarIcon – графический элемент в котором надо отобразить данные
      Для отображения моей коллекции я буду использовать UserControl, также можно использовать DataTemplate (об этом можно прочитать в по ссылке).
Сначала создаем объект <ResourceDictionary/> (подробнее про ресурсы) (словарь ресурсов), где и будет размещен наш TaskbarIcon.
Файл NotifyDictionary.xaml

<resourcedictionary 
     xmlns:local="clr-namespace:Wrapper" 
     xmlns:tb="http://www.hardcodet.net/taskbar"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <tb:taskbaricon iconsource="/Icons/Computers.ico"
     tooltiptext="Wrapper v.0.1"
     x:key="NotifIcon">
        <tb:taskbaricon .traypopup="">
            <local:usercontrol1>            
        </local:usercontrol1></tb:taskbaricon>
</tb:taskbaricon>
</resourcedictionary>
У объекта TaskbarIcon есть свойство TrayPopup, которое отображается при клике на иконку программы в трее. В это свойство мы и помещаем наш UserControl1. Далее нам надо подключить этот ResourceDictionary к приложению (файл App.xaml):

<application mc:ignorable="d" 
    startup="Application_Startup"
    x:class="Wrapper.App"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">   
    <application .resources="">
        <resourcedictionary>
            <resourcedictionary .mergeddictionaries="">
               "<resourcedictionary source="NotifyDictionary.xaml"></resourcedictionary>"
           </resourcedictionary>
        </resourcedictionary>
    </application>
</application>
Так как нам нужна дополнительная корректировка привязок в коде в App.xaml дописываем строчку с запускаемым методом (Startup="Application_Startup")
Файл App.xaml.cs:

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;
        }

Разберем поподробнее этот код:

понедельник, 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