понедельник, 10 октября 2011 г.

Code Contracts и Visual Studio Express

На сайте DevLabs (разработчиков Code Contracts) написано, что Visual Studio Express не поддерживается. Мне это показалось весьма странным, с учетом того, что System.Diagnostics.Contracts включен в .Net Framework 4. Конечно, Express версии не поддерживают PlugIn’ов, но ведь это совсем не повод, чтобы отказываться от использования CodeContract’ов, тем более, что все равно нужно выкачивать и устанавливать на машину разработчика то, что и называется CodeContracts. Установка содержит

  • ccrewrite утилита для осуществления проверки во время выполнения;
  • ccdoc утилита для генерации документации;
  • набор бибилиотек для .Net Framework’a 3.5 (по-моему, 3’й не поддерживается)

Премиум также содержит cccheck статический анализатор времени компиляции;

После установки в Visual Studio, в версиях выше Professional, в свойствах проекта появляется еще одна закладка: “Code Contracts”

 

image

Если отжать Perform Runtime Checking, то контракты начинаю срабатывать.

Если после этого открыть проект в Express версии (или в #Develope ), то контракты продолжат работать, не смотря на то, что закладки “Code Contacts” в свойствах проекта не появляется.

Итак, что же изменилось? А изменяется вот что: в файле проекта добавляются строки описывающие поведение контрактов.

Чтобы увидеть изменения нужно открыть сам файл проекта (.csproj для С#) в текстовом виде:

  • В VS Professional и выше: выгружаем проект в Solution Explorer,  правой кнопкой щелкаем на проект и выбираем Edit ProjectName.csproj;
  • в Express версии, просто лезем в папку с проектом и открываем любым простейшим текстовым редактором.

В каждую PrepertyGroup соответствующую вариантам сборки (X86/Debug/Release) добавились следующие строчки:

<CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
<CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
<CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
<CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
<CodeContractsRuntimeSkipQuantifiers>False</CodeContractsRuntimeSkipQuantifiers>
<CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
<CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
<CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
<CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
<CodeContractsEnumObligations>False</CodeContractsEnumObligations>
<CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
<CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
<CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs>
<CodeContractsCustomRewriterAssembly />
<CodeContractsCustomRewriterClass />
<CodeContractsLibPaths />
<CodeContractsExtraRewriteOptions />
<CodeContractsExtraAnalysisOptions />
<CodeContractsBaseLineFile />
<CodeContractsCacheAnalysisResults>False</CodeContractsCacheAnalysisResults>
<CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
<CodeContractsReferenceAssembly>%28none%29</CodeContractsReferenceAssembly>
<CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>

Привожу, за что какие параметры отвечают:

  • CodeContractsEnableRuntimeChecking: если значение установлено в true, ccrewrite будет вживлять в проект проверку условий контракта. Фактически проверки выполнения контрактов начинаются с установки  этого значения и для того чтобы разобраться с использованием достаточно включить только его; 
  • CodeContractsRuntimeCheckingLevel: устанавливает уровни использования контрактов, то есть можно включить проверку только пред или постусловий. Принимаемые значения: Full, Pre and Post, Preconditions, ReleaseRequires или None;
  • CodeContractsRuntimeThrowOnFailure: если установлено в true, то контракт будет выбрасывать исключение. Если false, то проверка контракта будет вести себя как Debug.Assert;
  • CodeContractsRuntimeOnlyPublicSurface: если установлено true, то обрабатываются только публичные методы;
  • CodeContractsRuntimeCallSiteRequires: когда установлено true, реврайтер внедряет контракты в вызывающие ваш код приложения; ;
  • CodeContractsReferenceAssembly: принимает значения {Build, DoNotBuild, None}. Если выставить в Build, то создается дополнительная сборка с набором контрактов для этого проекта. Это нужно если на ваш проект ссылается еще один и ему нужно видеть, какие контракты используются или если вы хотите генерировать документацию по контрактам;
  • CodeContractsEmitXMLDocs: если установлено в true вместе с CodeContractsReferenceAssembly, генерируемая документацию к сборке будет содержать описание контрактов;
  • CodeContractsCustomRewriterAssembly: указывает пусть к сборке содержащей свою реализацию методов реврайтера;
  • CodeContractsCustomRewriterClass: содержит имя класса в котором реализованы методы, используется вместе с CodeContractsCustomRewriterAssembly;
  • CodeContractsLibPaths: здесь указываются через точку с запятой  пути к дополнительным сборкам содержащим контракты;
  • CodeContractsRunCodeAnalysis: значение включает статическую проверку кода собираемого проекта;
  • CodeContractsNonNullObligations: когда установлено true, статический анализатор будет пытаться проверять все ссылки на корректность;
  • CodeContractsBoundsObligations: когда установлено true, статический анализатор будет пытаться проверять корректность указания границ массивов;
  • CodeContractsArithmeticObligations: когда установлено true, статический анализатор будет пытаться проверять корректность математических операций, таких как деление на ноль;
  • CodeContractsRedundantAssumptions: когда установлено true, статический анализатор будет выдавать warning’и при “очевидных сравнениях” (не знаю как правильно перевести Redundant Assumptions) ;
  • CodeContractsRunInBackground: если установлено в true,  статический анализатор работает в фоновом процессе, не блокируя сборку проекта;
  • CodeContractsExtraAnalysisOptions: используется для передачи дополнительных параметров статического анализатора;
  • CodeContractsRuntimeSkipQuantifiers: если установлено в true, то некоторые контракты содержащие вызовы Contract.ForAll и Contract.Exists пропускаются. Это дает большую разницу в производительности;
  • CodeContractsEnumObligations: если установлено в true, статический анализатор проверяет границы перечислений; 
  • CodeContractsShowSquigglies: включает подчеркивание волнистой линией ошибок;
  • CodeContractsUseBaseLine: включает так называемую БазовуюЛинию. Она используется для того, чтобы статический анализатор мог отслеживать предупреждения только в изменяемом коде. При включении генерируется файл содержащий предупреждения и при последующих компиляциях они не отображаются;
  • CodeContractsBaseLineFile: имя файла в который записываются предупреждения;
  • CodeContractsExtraRewriteOptions: содержит дополнительные параметры для ccrewrite;
  • CodeContractsCacheAnalysisResults: включает кэширование статическим анализатором результатов;
  • CodeContractsAnalysisWarningLevel:  устанавливает уровень эвристики статического анализатора.

Большинство этих параметров вносить не обязательно. Параметры, которые относятся к статическому анализатору можно точно откинуть, а остальные, за исключением CodeContractsEnableRuntimeChecking имеют вполне рабочие значения по умолчанию. Если вы хотите избавить свой проект от нагромождений if…throw,то достаточно использовать  CodeContractsEnableRuntimeChecking.

Например.

Создаем в Visual Studio Express новый проект консольного приложения с таким кодом:

static void Main(string[] args)
{
    PrintMessage("Привет");
    Console.ReadLine();
}

static void PrintMessage(string str)
{
    Contract.Requires(str.Length > 10,"Message to short");
    Console.WriteLine("Сообщение: {0}",str);
}

Если приложение запустить, оно отлично отрабатывает, не смотря на то, что мы указали в контракте, что длина сообщения должна быть больше 10 символов.

Сохраняем, закрываем VS Express, вносим изменения в файл проекта, снова открываем проект в VS Express.

image

Контракты начинают срабатывать.

вторник, 2 августа 2011 г.

Сохранение порядка сортировки выборки в Linq

На MSDN’овском форуме задали вопрос о сохранении порядка сортировки выборки при использовании Linq. Задача достаточно типовая, поэтому решил написать небольшой мануал, как это сделать используя Dynamic Linq. Для упрощения пусть данные отображается у нас в DataGrid’e и сортируются нажатием мышкой на заголовок столбцов.

Dynamic Linq

Библиотека Dynamic Linq  входит в набор примеров кода для Visual Studio 2008 (C#, VB). После выкачивания можно найти исходный код, документацию и пример использования в папке \LinqSamples\DynamicQuery.

Проект с примером использования отлично собирается как под .Net Framework 3.5, так и под 4.0.  Сам код библиотеки вынесен в namespace System.Linq.Dynamic и находится в файле Dynamic.cs. Для использования можно скопировать код к себе в проект или создать отдельный проект с библиотекой.

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

Итак у нас есть форма, на ней DataGrid с гордым именем dataGrid1.

Простой класс:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string NickName { get; set; }
    public DateTime BirthDate { get; set; }
}

При инициализации формы подсовываем Grid’у в качестве источника данных массив Person:

var persons = new Person[] 
{
    new Person{FirstName = "Сидр", LastName = "Сидоров", NickName = "sid", BirthDate = DateTime.Today.AddYears(-22)},
    new Person{FirstName = "Петр", LastName = "Петров", NickName = "pit", BirthDate = DateTime.Today.AddYears(-21)},
    new Person{FirstName = "Иван", LastName = "Иванов", NickName = "ian", BirthDate = DateTime.Today.AddYears(-20)},
};
this.dataGrid1.ItemsSource = persons;

Данные отображаются и сортируются нажатием на заголовки колонок, но порядок сортировки не сохраняется между сеансами.

Итак, приступим.

Первое, что нам нужно сделать, это перенести код библиотеки Dynamic Linq  к себе в проект. Это можно сделать двумя способами:

  1. Создать класс и скопировать код из полученного ранее проекта;
  2. Сразу добавить файл в себе в проект (в Solution Explorer’e щелкнуть право кнопкой на проект, и выбрать Add->Existing Item).

У DataGrid’а добавляем в обработчик события Sorting код, который будет сохранять поле и порядок сортировки в конфигурационном файле

private void dataGrid1_Sorting(object sender, DataGridSortingEventArgs e)
{
    IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly();
    using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("LastSort.txt", FileMode.Create, store))
    {
        using (StreamWriter writer = new StreamWriter(stream))
        {
            writer.WriteLine(String.Format("{0} {1}", e.Column.SortMemberPath,
                (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending));
        }
    }
}

Небольшое пояснение по поводу IsolatedStorage
IsolatedStorage – это механизм позволяющий сохранять данные в “виртуальные” папки в зависимости от выбранной области ограничения видимости. Использованная функция GetUserStoreForAssembly() возвращает нам “путь” который уникален для пользователя  и Assembly, т.е. другие приложения и пользователи не будут иметь доступ к файлу LastSort.txt в котором хранятся данные об используемом порядке сортировки.

Небольшое пояснение по поводу (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending
Дело в том, что Column.SortDirection является Nullable, то есть кроме ListSortDirection.Ascending и ListSortDirection.Descending принимает еще и значение NULL, которое и является значением по умолчанию. Помимо этого в  DataGridSortingEventArgs e содержится предыдущее состояние сортировки, поэтому приходится использовать такую конструкцию.

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

private void Window_Closing(object sender, CancelEventArgs e)
{
    foreach (var c in this.dataGrid1.Columns)
    {
        if (c.SortDirection.HasValue)
        {
            IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly();
            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("LastSort.txt", FileMode.Create, store))
            {
                using (StreamWriter writer = new StreamWriter(stream))
                {
                    writer.WriteLine(String.Format("{0} {1}", c.SortMemberPath, c.SortDirection));
                }
            }
            break;
        }
    }
}

Пишем функцию которая выдергивает значение из файла в изолированном хранилище

private string GetLastSort()
{
    IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly();
    using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("LastSort.txt", FileMode.OpenOrCreate, store))
    {
        using (StreamReader reader = new StreamReader(stream))
        {
            return reader.ReadLine() ?? "NickName Descending";
        }
    }
}

После этого меняем подключение данных для DataGrid’а на

this.dataGrid1.ItemsSource = persons.AsQueryable<Person>().OrderBy(GetLastSort()).Select("new (FirstName, LastName, NickName, BirthDate)");

среда, 20 апреля 2011 г.

Экзамен 70-599: Pro: Designing and Developing Windows Phone 7 Applications, Часть 5: Designing the User Interface and User Experience

>>В начало

Designing the User Interface and User Experience (23%)

  • Design for separation of concerns.
    • This objective may include but is not limited to: presentation patterns that use view models, MVVM
  • Design Windows Phone 7 control usage.
    • This objective may include but is not limited to: design control usage as described in UI Design and Interaction Guide for Windows Phone 7; design proper use of PanoramaControl and PivotControl; choose when to use the Panorama Control and PivotControl; recommend when to use ApplicationBar
  • Recommend keyboard layout for a given situation.
    • This objective may include but is not limited to: InputScope property
  • Design for system themes, accent color, and screen orientation.
    • This objective may include but is not limited to: built-in styles that use system themes and accent colors, ApplicationBar icons (size, transparency), landscape, portrait

Ссылки:

Designing the User Interface and User Experience (23%)

RU links: Video (EN)

Экзамен 70-599: Pro: Designing and Developing Windows Phone 7 Applications, Часть 4: Designing the Application Architecture

>>В начало

Designing the Application Architecture (21%)

  • Design for threading.
    • This objective may include but is not limited to: use of the composition thread
  • Monitor and tune performance.
    • This objective may include but is not limited to: frame rate counter; cache visualization; redraw regions; bitmap caching; memory usage limitations; plan for power consumption; tune bandwidth consumption; performance counters
  • Manage the application life cycle.
    • This objective may include but is not limited to: tombstoning; response to PhoneApplicationService events (Launching, Activated, Deactivated, Closing)
  • Prepare the application to meet Windows Phone 7 marketplace requirements.
    • This objective may include but is not limited to: Windows Phone 7 Application Certification Requirements; design for localization and globalization; plan for trial versions; work with WMAppManifest.xml; design for icon requirements for marketplace

Ссылки:

Designing the Application Architecture (21%)

RU Links:

Other links:

Video (EN):

Экзамен 70-599: Pro: Designing and Developing Windows Phone 7 Applications, Часть 3: Working with Platform APIs, Tasks, and Choosers

>>В начало

Working with Platform APIs, Tasks, and Choosers (21%)

  • Design and implement sensor interaction.
    • This objective may include but is not limited to: choose which sensors are appropriate for your application; design location awareness (when to use different levels of GeopositionAccuracy); location awareness system setting
  • Plan for and implement the use of Tasks and Choosers.
  • Plan for and implement multitouch and gestures.
    • This objective may include but is not limited to: manipulation events (ManipulationStarted, ManipulationCompleted, ManipulationDelta)
  • Design and implement application navigation.
    • This objective may include but is not limited to: pass parameters (NavigationContext API), manipulate the navigation stack (NavigationService API), use of the Back button, PhoneApplicationPage class and PhoneApplicationFrame class and the difference between these two classes

Ссылки:

Working with Platform APIs, Tasks, and Choosers (21%)

RU Links:

EN Links:

Video (EN):

Экзамен 70-599: Pro: Designing and Developing Windows Phone 7 Applications, Часть 2: Designing and Implementing Notification Strategies

>>В начало

Designing and Implementing Notification Strategies (17%)

  • Plan for and implement push notifications in the application.
    • This objective may include but is not limited to: choose method for notifying user of application’s state/status (tile, toast, RAW); respond to notifications; registration for notifications
  • Plan for and implement push notifications on the server.
    • This objective may include but is not limited to: when to use toast, tile, and raw; plan for receiving the unique device URL
  • Create and update live tiles.
    • This objective may include but is not limited to: update background image, numbers, and text

Ссылки:

Designing and Implementing Notification Strategies (17%)

RU Links: EN Links: Video (EN):

Экзамен 70-599: Pro: Designing and Developing Windows Phone 7 Applications, Часть 1: Designing Data Access Strategies

>>В начало

Designing Data Access Strategies (19%)

  • Send and receive data.
    • This objective may include but is not limited to: design connection mechanisms for communicating with external web services; plan how to consume and parse data from web services (for example, WCF and WS*); ensure a trusted transfer of data to and from a phone
  • Design a data storage strategy.
    • This objective may include but is not limited to: differentiate between persistent and transient data; determine when to use isolated storage; plan for size limitations of isolated storage; design cloud-based storage
  • Plan for bandwidth limitations and implement network connectivity detection.
    • This objective may include but is not limited to: plan for disconnected scenarios; plan for low network bandwidth

Ссылки:

Designing Data Access Strategies (19%)

RU Links: Video (EN):