Динамическое создание Windows и Web CRUD-интерфейсов и не только для бизнес приложений с XAF + Entity Framework. Часть 1

Предыстория


У нас есть старенькое веб приложение DXLibrary для учета литературы и поиска интересных пополнений в местной библиотеке, написанное много лет назад за пару часов одним из разработчиков на DevExpress eXpressApp Framework (XAF). Данные приложения хранятся в Microsoft SQL Server базе, а работа с ними ведется через DevExpress eXpress Persistent Objects ORM (XPO). Так как сие чудо до сих пор сносно выполнет свои незаурядные функции, так что мне захотелось немного освежить его интерфейс под текущие реалии. Помимо фейслифтинга хочется сменить XPO на Microsoft ADO.NET Entity Framework (EF) под капотом.image Основным мотивом тут для меня является не то, что EF чем-то лучше или хуже (обе ORM давно доказали, что отлично подходят для своих задач), а то, что до сих пор на Хабре не было русскоязычного туториала по интеграции XAF и EF, хотя они уже «не первый год замужем». Есть огромное желание восполнить этот пробел + элементарно было несколько запросов от пользователей «библиотекаря», которые можно заодно запилить в новой версии. Наконец, с версии 15.1 XAF предоставляет обновлённый веб интерфейс, заточенный в том числе для touch-устройств, который хотелось бы лишний раз показать сообществу, что греха таить:-)

Это первая и вводная часть из серии постов, где я попытаюсь показать по шагам вариант ускоренной генерации как веб, так и настольных line-of-business (LOB) приложений по существующей базе либо готовой модели Entity Framework, представляющей структуру данных вышеописанной предметной области. Пользовательский интерфейс будет создаваться автоматически на базе микса технологий ASP.NET WebForms/HTML5 и WinForms, а также соответствующих визуальных компонент DevExpress, скомпонованных вместе в рантайм под чутким руководством XAF — нашего UI фреймворка (узнать больше на Хабре, сайте), а по сути сложившегося за почти 10 лет существования и постоянно расширяемого набора best patterns & practices для быстрого создания Office-like бизнес приложений для .NET разработчиков.

Минимальные требования для прохождения


1. .NET Framework 4+ и Visual Studio 2010+ (поддерживаются полные и Community Edition версии, кроме express) — далее я буду использовать Visual Studio 2013.
2. Entity Framework 6 Tools (в зависимости от версии студии могут быть уже встроенными либо требовать отдельной установки)
3. Полня версия DevExpress XAF v15.1 (а лучше 15.2, если есть доступ к бете), либо 30-дневная пробная, которая качается с нашего сайта бесплатно, без регистрации и СМС:-) и ставится за пару минут на SSD.
4. Microsoft SQL Server для хранения данных (подойдут любые полные/express/localdb версии начиная с 2000) либо другой провайдер БД, поддерживаемый EF.

Перед тем как начать, мои размышления на тему такого рода фреймворков, возможностей кастомизации и выбора ORM
Важно понимать, что XAF по своей сути не является code generator, как, например, наши похожие продукты DevExpress MVVM Framework & Scaffolding Wizard для WPF/WinForms. Напротив, XAF является самым настоящим динамическим application framework, который генерит пользовательский интерфейс по неким правилам полностью во время исполнения. Так вам не придется руками перезапускать генерацию форм после каждого изменения вашей исходной модели данных (таких как добавление нового класса или свойства) или терять ранее написанный в сгенерированных формах кастомный код — после запуска XAF приложения вы всегда автоматически получите актуальный результат скаффолдинга UI и БД по вашей текущей структуре данных и набору правил. Практически это означает, что по умолчанию в XAF не генерится никакой исходный код CRUD форм, к которому разработчик бы имел прямой доступ в студии, как это происходит в обычной разработке клиентских приложений. Хотя разработчик XAF все-таки имеет полный контроль над конечными кирпичикам UI типа System.Windows.Forms.Control или HTML элементами и может ими манипулировать как в любом другом .NET приложении, большую часть времени натуральный процесс XAF разработки представляет из себя настройку системы на более высоком уровне, часто через структуры дата моделей, кросс-платформенные метаданные или абстракции типа View, Property Editor, Controller и др. Повторюсь, что доступ на низкий уровень есть, вас никто не ограничивает, можно также миксовать подходы, но требуется это намного реже, а иначе какой тут выигрыш по сравнению с традиционной разработкой? Должен признаться, что в этом и заключается одновременно самая большая прелесть и недостаток XAF, так как поначалу людям сложно перестроиться и абстрагироваться от оперирования визуальными контролами напрямую. Отсюда и крутая кривая обучения на первых этапах.

Наверное, также сразу стоит развеять сомнения по поводу возможностей UI и кастомизации под различные нужды заказчика в такого рода RAD тулзах, как говорится, не квадратно-гнездовым единым! Чтобы лучше представить UI типовых бизнес приложений, которые могут быть созданы с помощью фреймворка, можно посмотреть готовые скриншоты на страничке продукта тут, потыкать вживую демки онлайн (или лучше из инсталляции, чтобы и десктопные демки охватить заодно) и, наконец, поглядеть в Интернете примеры продакшн приложений от реальных заказчиков, например, вот хороший пример или вот парочка от знакомой многим в СНГ Галактики с их AMM, CnP, EAM и др. решениями (раз, два). Последние, кстати, вышли из «галактического» тюнинг-ателье:-) XAFARI — набора дополнительных модулей и компонет под платформу XAF. Помимо коммерческих third-party расширений, нельзя не упомянуть и бесплатный проект http://www.expandframework.com, который уже 5 лет развивается силами сообщества. Тут же отмечу, что во многом благодаря использованию визуальных компонент DevExpress работать с созданными приложениями вполне возможно, как и на обычном десктопе, так и на touch-устройствах с меньшим экраном, например, планшетах, при минимуме дополнительных настроек со стороны разработчика (причем даже в старом-добром WinForms!), но об этом позже.

Если у вас еще нет готового багажа знаний какой-то ORM, то прежде чем начать работу с XAF, вас наверняка будет мучить сложный выбор. С одной стороны, имеем дело с XPO — простой и до сих пор здравствующей проприетарной ORM библиотекой (узнать больше из русскоязычных обзоров сообщества: раз, два), которая c 2006 года была единственной выбором для XAF, с огромным числом примеров, документации, видео, бесплатной гарантированной поддержкой от компании-разработчика, а с другой — рекомендуемая опенсорсная технология для доступа к данным для .NET с бОльшим числом разработчиков на рынке труда, хорошим продвижением и развитием от Microsoft, поддержкой сторонних компаний, а также не меньшей базой знаний и технической поддержкой силами сообщества на StackOverFlow, Social.MSDN, forums.asp.net (правда, без гарантии быстрого ответа либо ответа вообще). Мы проводили опросы среди своих новых пользователей и оказалось, что для многих при прочих равных довольно существенным фактором при выборе технологии оказывается наличие гарантированной технической поддержки (здесь имеется ввиду помощь в случае проблем или вопросов по тому как лучше сделать), а не факт, что где-то там больше или меньше каких-то функций. Технически, если не брать во внимание факт, что формально EF поддерживается XAF позже XPO — с 2012 года, то поддержка двух библиотек на уровне самого XAF и его модулей сравнима по функционалу и охватываемым сценариям. В данный момент доля наших EF пользователей растет, но все еще прилично уступает XPO. Думается, релиз EF7 и также выравнивание по наличию примеров на нашей стороне еще изменит данное соотношение. Больше мыслей и отзывов наших пользователей на данную тему можно найти в моем блоге по теме.

Шаги по созданию нового XAF решения с Entity Framework 6


0. Скачаем пробную версию с нашего сайта, дождемся успешной установки и запустим Visual Studio;

1. Запустим мастер создания нового проекта с именем DXLibraryV2 через стандарное FILE | New | Project…, воспользовавшись поиском и выбрав XAF Solution Wizard (либо изначально вызвав специальное DEVEXPRESS | All Platforms | New Project… меню студии)

(показать…)
41797ecca63849b89af60664e4ae08ca.png


2. В запустившемся мастере выберем платформу Windows и Web и нажмем Next

(показать…)
dafd0019c7e342eeba5e1be3c65caf72.png


3. Выберем Entity Framework Code First для доступа к данным и нажмем Next

(показать…)
869cbed8f7cf4bc8af69d5079ff05359.png


4. Выберем Authentication = Active Directory, что означает вход в приложение по текущему Windows пользователю и нажмем Next

(показать…)
96cb1f3ef7f04d33a98fcf6663a6a766.png


5. Завершим работу нажатием на Finish, так как пока нам не нужны больше никакие дополнительные модули

(показать…)
5c9d35e7eb3a40a4912dd52f38a97314.png

Основные части созданного XAF решения


В результате визард создаст нам заготовки для будущих приложений согласно выбранным ранее настройкам:

5bca54653cec470ba7012477c0e9e7d4.png

В двух словах, у нас получилось:

  • DXLibraryV2.Module — один кросс-платформенный модуль, содержащий шаренный код для всех платформ (например, бизнес логика, модели данных, настройки метамодели UI, работа с базой и в редких случаях универсальные UI редакторы)
  • DXLibraryV2.Module.Wxx — пара платформенно-зависимых модуле, где могут жить код и настройки, специфичные для какой-то платформ
  • DXLibraryV2.Wxx — по одному исполняемому приложению на каждую платформу, т.е. ASP.NET сайт и WinForms приложение. По умолчанию эти приложения будут соединяться с Microsoft SQL Server для хранения данных по connectionString из конфигурационных файлов приложений (Web.config и App.config).


Для удобства, проекты содержат подпапки с говорящими именами и readme.txt файлы, которые могут помочь не заблудиться даже самым суровым новичкам (узнать больше…).

Если мы прямо сейчас запустим DXLibraryV2.Web и DXLibraryV2.Win из Visual Studio, то получим работающие болванки приложений для веб браузера (ASP.NET WebForms) и десктопа (WinForms), которые пока ничему особенному не обучены, ну разве что кроме CRUD управления пользователями, ролями и настройками системы безопасности — эдакая простенькая админка за минуту:

dd5b46be9f4d4afe9e37796a05316974.png
27935da2062944449144e57c5193b532.png

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

Особенности Entity Framework интеграции


Прежде всего, основная поддержка Entity Framework в XAF реализована в сборках DevExpress.ExpressApp.EF.v15.1.dll (для .NET 4.0) и DevExpress.ExpressApp.EF.45.v15.1.dll (для .NET 4.5), которые автоматически ссылаются в XAF проектах в зависимости от версии .NET Framework. Если вы используете наш мастер по созданию проектов, то все необходимые EF зависимости добавляются автоматически. Если нет, то их всегда можно добавить в проекты стандартным способом через NuGet.

На момент написания статьи последняя XAF версия поддерживает Entity Framework 6, охватывая как code-based стратегии c использованием DbContext так и устаревшую стратегию на базе ObjectContext и EntityObject (в старых версиях также есть поддержка EF 5). По статистике, среди наших пользователей популярность последнего подхода к разработке модели данных сильно уступает первому, а после слухов о вероятном убиении EDMX в следующей генерации EF думаю она вообще сойдет на нет. Официальной поддержки последних бет EF 7 на данный момент у нас нет ввиду постоянных изменений, хотя уже ведутся работы в данном направлении. Сама по себе новая генерация EF уже сейчас содержит много интересных возможностей для нас и наших пользователей и выглядит очень перспективной.

Технически, работа с EF Code First подразумевает, что у нас должны быть POCO бизнес-классы, определяющие структуры нашей модели данных, и наследник от DbContext, содержащий необходимый набор типизированных коллекций бизнес сущностей DbSet, которые могут быть запрошены из базы. Вы еще помните, что на 5 м шаге мы выбрали тип аутентификации пользователей (AuthenticationActiveDirectory), тем самым активировав встроенный модуль системы безопасности (SecurityModule)? Так вот, в результате этого мастер проекта включил в нашего наследника DbContext (DXLibraryV2.Module\BusinessObjects\DXLibraryV2DbContext.cs) необходимые встроенные сущности User, Role (а также др. сервисные запчасти), объявленные в опциональной библиотеке DevExpress.Persistent.BaseImpl.EF.v15.1, идущей как часть стандартной поставки XAF и содержащей готовые классы для типовых случаев.

d461b3fb6fb54bc69773e5d74e1ad4ca.png

Вышеупомянутые сущности являются совершенно обычными POCO классами, определяющими какую-то структуру данных и логику, например:

b98e2c9a63ed442eb8aa71aff11cb3fc.png

Далее тип пользовательского DbContext либо ObjectContext (в случае EDMX стратегии) вместе с выбранной строкой соединения к базе данных, взятой из конфигурационных файлов исполняемых приложений, передается в EFObjectSpaceProvider при настройке доступа к данным в наследниках WebApplication (DXLibraryV2.Web\WebApplication.cs) или WinApplication (DXLibraryV2.Win\WinApplication.cs).

public partial class DXLibraryV2WindowsFormsApplication : WinApplication {
    public DXLibraryV2WindowsFormsApplication() {
        InitializeComponent();
    }
    protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
        args.ObjectSpaceProvider = new EFObjectSpaceProvider(
            typeof(DXLibraryV2DbContext),
            TypesInfo, null, 
            args.ConnectionString
        );
    }


WxxApplication классы представляют из себя исполняемые приложения для каждой из платформ и связанные с ними сервисы. Их экземпляры создаются и настраиваются при каждом запуске во входных точках приложений, например в методе Session_Start из DXLibraryV2.Web\Global.asax.cs или методе Main из DXLibraryV2.Win\Program.cs. Среди необходимых настроек приложения помимо компонентов системы безопасности и строки соединения присутствует список зависимых модулей, который может состоять как из сторонних так и из наших собственных модулей, например типа DXLibraryV2Module (DXLibraryV2.Module\Module.cs). XAF модули (наследники от ModuleBase) как и сами приложения (наследники XafApplication) являются настраиваемыми производными от System.ComponentModel.Component, для которых также доступны визуальные дизайнеры.

c523d0102de0453f91b85ca28410d263.png

Специально для первого запуска приложений мастером был закодирован тестовый набор данных в методе UpdateDatabaseAfterUpdateSchema наследника ModuleUpdater (DXLibraryV2.Module\DatabaseUpdate\Updater.cs) — аналога метода Seed у EFских database initializerов.

d85e7a83068b491b8520be1bddf90e1a.png

В результате отработки методов ModuleUpdater-ов и связанного кода фреймворком была создана новая база данных на Microsoft SQL Server и соответствующие таблицы с записями:

92fe43a9d362498a924efbc48baca605.png

EFObjectSpaceProvider независимо от выбора code-based или EDMX стратегий работает с ObjectContext API, являющимся для них общим знаменателем, ведь внутри DbContext в EF 6 пока еще содержит в себе ObjectContext, доступ которому можно получить через приведение к IObjectContextAdapter. В частности, мы используем его MetadataWorkspace для рефлексии информации о типах и их структуре для дальнейшего построения скелета CRUD пользовательского интерфейса.

Как вы могли заметить из примера кода в ModuleUpdater выше, в контексте XAF CRUD операции рекомендуется производить не напрямую через DbContext/ObjectContext, а через эту абстрактную сущность IObjectSpace или в нашем случае ее конкретную реализацию для Entity Framework — EFObjectSpace. Он поставляется вышеупомянутым EFObjectSpaceProvider и использует у себя внутри ObjectStateManager и CreateQuery API, чтобы модифицировать или считывать данные. IObjectSpace по сути является ORM-независимым воплощением паттернов Repository и Unit Of Work, а технически просто оберткой над контекстом данных. Работая через IObjectSpace API, вы можете раз написав какую-то бизнес логику, мигрировать ее на другую ORM без изменений. Более важно, что посредством IObjectSpace интерфейсная часть XAF взаимодействует с данными, например, получает их или «слушает» изменения и обновляет себя соответственно.

Практические заметки на будущее


1. Во время разработки и отладки приложения с тестовой БД после постоянных правок структуры дата модельных классов удобно временно отключать Code First Migrations фичу ЕF, прописывая нужный database initializer с помощью встроенного API из System.Data.Entity, например вот так (DXLibraryV2.Module\Module.cs):

// Uncomment this code to delete and recreate the database each time the data model has changed.
// Do not use this code in a production environment to avoid data loss.
// #if DEBUG
// Database.SetInitializer(new DropCreateDatabaseIfModelChanges());
// #endif

2. Совсем не обязательно использовать встроенные классы User, Role из DevExpress.Persistent.BaseImpl.EF.v15.1– это в основном нужно новичкам для быстрого «взлета». XAF не является конечным продуктом и, в первую очередь — это гибкий и расширяемый *фреймворк* для построения приложений, в котором можно всегда заменить нужные запчасти на свои либо начать делать все «с нуля», если уже знаешь что да как. Так, например, вместо встроенных запчастей системы безопасности мы могли запросто объявить свои сущности согласно EF документации и добавить их в наш DbContext.

3. Для нормальной работы многих стандартных сценариев желательно реализовывать поддержку INotifyPropertyChanged в ваших бизнес классах (хоть вручную хоть с помощью утилит типа этой, которые автоматически инжектят нужную реализацию во время компиляции). На худой конец можно вручную реализовать наш спец. интерфейс IObjectSpaceLink и руками звать ObjectSpace.SetModified (this), чтобы прокинуть нотификацию в нужных местах.

4. Из опыта, клиенты могут (и хотят) размещать бизнес логику прямо в самих EF классах вместо реализации отдельных контроллеров или сервисов. Не в нашем праве им запрещать (хотя на тему анемичной модели vs богатой на Стеке куча холиваров, помню даже сам вбросил свои пять копеек тут) и для таких случаев помимо вышеупомянутого IObjectSpaceLink может быть удобен интерфейс IXafEntityObject, который облегчает написание логики на создании (OnCreated), загрузке (OnLoaded) и сохранении сущностей (OnSaving).

5. Если у вас осталась готовая библиотека EF классов от ASP.NET MVC проекта, вам может быть важно знать, что в данный момент у нас ограниченная поддержка стандартных Data Annotations атрибутов влияющих на вид интерфейса, например DisplayFormat, UIHint. При этом стандартные фреймворковские атрибуты влияющие на поведение самого EF работают как и должны. Как бы там ни было, в XAF есть куча своих универсальных атрибутов на любой вкус и цвет, которые позволяют ускорить настройку UI и связанной логики в простых случаях. Забегая вперед стоит отметить, что в общем для такой задачи предпочтительнее использовать Application Model (вот тут уже рассказывал детальнее).

В заключение


Это, наверное, все основные особенности интеграции Entity Framework в XAF, о которых нужно знать на данном этапе. Углубить свое понимание можно с помощью нашей онлайн документации тут: eXpressApp Framework > Concepts > Business Model Design > Business Model Design with Entity Framework и eXpressApp Framework > Getting Started > Basic Tutorial (тут также содержится информация о фреймворке и основных подходах в общем). Также могут пригодиться демки XCRM, EFDemoCodeFirst, EFDemoModelFirst, которые можно найти в %Public%\Documents\DevExpress Demos 15.1\Components\eXpressApp Framework\ и которые представляют из себя довольно сложные и законченные приложения, демонстрирующие интеграцию почти со всеми модулями фреймворка. Куча примеров и ответов в базе знаний техподдержки также не будут лишними к упоминанию.

В следующей серии мы будем непосредственно создавать модель данных EF по существующей базе данных и также реализовывать основную логику работы исходного приложения-прототипа.

C уважением,
Денис

P.S.


Если вас заинтересовали наши продукты и после их более подробного изучения в течение бесплатного пробного периода вам захочется их приoбрeсти, то до 20 декабря 2015 года включительно вы можете воспользоваться дeсяти пpoцeнтным кyпонoм на пoкyпку в рyблях (~$220 или ~14тыc рyблeй по текущему курсу) новой лицензии DevExpress Universal, включающей в себя XAF и кучу других инструментов для разработки под популярные платформы. Для этого вам нужно будет перейти в мaгaзин Softline.ru и после оформления зaкaза

ввeсти в специальное поле кoд

Universal_XafEf


Отдельное спасибо нашему рeceллеру за то, что достаточно быстро удалось договориться об этой aкции на благо русскоязычного сообщества СНГ в такое время. Если вам вдруг удобнее кyпить в вaлюте напрямую с нашего сайта, то для получения аналогичной cкидки, напишите мне на почту с указанием ссылки на этот пост. Туда же можете присылать любые вопросы по XAF, пожелания или информировать о возможных проблемах.

© Habrahabr.ru