Upload
gosharp
View
346
Download
3
Embed Size (px)
Citation preview
1
На примере текстового редактора ASPxRichEdit
Опыт разработки сложных
клиент-серверных приложений на
TypeScript и ASP.NET
Developer Express Inc.
Роман Решетников
Ведущий разработчик команды ASP.NET
2Go
# C
on
fere
nce
s
Продукты DevExpress - для множества платформ
3Go
# C
on
fere
nce
s
Продукты DevExpress - для всех устройств
4Go
# C
on
fere
nce
s
Продукты команды ASP.NET
5Go
# C
on
fere
nce
s
Офисные контролы ASP.NET - ASPxSpreadSheet
6Go
# C
on
fere
nce
s
ASPxRichEdit – процессор rich-text документов
Работа со всеми популярными rich-text форматами
(DOCX, DOC, RTF, TXT, MHT, ODT, EPUB и т.д.)
Нативная работа с rich-text документами без потери
содержимого
Поддержка стилей, настроек секций, форматирования
текста и параграфов
Печать, экспорт в PDF
Знакомый UI вдохновленный продуктами MS Office
7Go
# C
on
fere
nce
s
ASPxRichEdit – не очередной HTML редактор
Rich-Text Editor HTML Editor
Постраничная разбивка
Настраиваемые поля
Поддержка колонок
Header и Footer
Поддержка содержания
Стили документа
Форматирование текста
Поддержка параграфов
Принцип работы Полный контроль над
документом
Нативная браузерная
функциональность
Формат документа RTF, DOCX, DOC, ODT… HTML
8Go
# C
on
fere
nce
s
RichEdit для десктопных платформ: WinForms, WPF
30+MB исходного кода (базового для всех платформ)
9000+ тестов базового кода
Более 8 лет разработки
Язык разработки C#
9Go
# C
on
fere
nce
s
Выбираем инструменты TypeScript
Jasmine + Chutzpah
Перенос логики на
клиент
Синхронизация с
сервером
Integration тесты
Генерация кода
Асинхронность в JS
Сервис/хандлер вместо привычных
ASP.NET коллбеков
Синхронизируем реквесты, версионность
Тестируем синхронизацию
Как перенести код с сервера на клиент?
10
Инструменты
TypeScript – язык, позволяющий работать со сложной типизированной моделью
Тестирование клиентского кода
11Go
# C
on
fere
nce
s
TypeScript для .NET разработчика
Строгая типизация – необходимость для приложений со
сложными моделями
700+ классов в TS проекте
5000+ классов в .NET библиотеке
Компиляция в «чистый» .JS
Размер скомпиленного .js кода на 15% больше размера .ts.
Простая отладка js кода даже без .map файлов
Хорошая поддержка Visual Studio
Простой синтаксис подобный C#
public class CharacterFormattingInfo {
public string FontName { get; set; }
public int Size { get; set; }
public bool Bold { get; set; }
public bool Italic { get; set; }
public StrikeoutType StrikeoutType { get; set; }
public UnderlineType UnderlineType { get; set; }
public bool AllCaps { get; set; }
…
export class CharacterFormattingInfo {
fontName: string;
size: number;
bold: boolean;
italic: boolean;
strikeoutType: StrikeoutType;
underlineType: UnderlineType;
allCaps: boolean;
…
C# код
TypeScript код
12Go
# C
on
fere
nce
s
Но не все идеально в TypeScript
Отсутствует автоматическая линковка .ts файлов.
Компилятору надо подсказывать в каком порядке их
склеивать
Сложно выделить .ts файлы с тестами за пределы
основного TypeScript кода
TS классы не линкуются между проектами Visual Studio
Медленный и не всегда корректный анализ ошибок
компиляции в VS (на больших проектах)
Как вынести тесты в отдельный проект и не потерять связь
между типами:
<TypeScriptCompile Include="{Путь-до-базового-проекта}\**\*.ts"><Link>_referencesTS\%(RecursiveDir)%(FileName)</Link>
</TypeScriptCompile>
13Go
# C
on
fere
nce
s
Покрываем клиентский код тестами
Jasmine – удобный framework для BDD unit тестирования
Встроенный framework для быстрого создание mock функций
Простое тестирование timeout функций
«Читабельный» синтаксис
Совместим с TypeScript
Chutzpah – runner тестов в Visual Studio
Простой запуск тестов прямо в VS
Сам создает необходимое окружение для запуска теста в
браузере
Поддерживает тесты написанные на TypeScript и Jasmine
14
Перенос логики на клиент
Перенос серверных типов на клиент
Очереди процессов вместо асинхронности
15Go
# C
on
fere
nce
s
Быстрый отклик – когда все на клиенте
Клиент работает независимо от сервера. Сервер только
уведомляется о новом состоянии модели
Для продолжения работы с документом клиенту не нужен
ответ сервера
UI не блокируется на время request/response
Модель отправляется на клиент частями
Вся модель разбивается на небольшие части – chunks
Вместе с рендером страницы отправляется первый chunk
Остальные chunks отсылаются на клиент асинхронно
16Go
# C
on
fere
nce
s
Передача модели между сервером и клиентом
Сервер
Model→JSON
Response
JSON→Model
Клиент
Model→JSON
Request
JSON→Model
17Go
# C
on
fere
nce
s
TextTemplates для генерации JSON конвертеров
RichEdit.Core DLL
TextTemplate Engine
• Type FindType(string
typeName)
• PropertyInfo[]
GetProperties()
Converters
• .NET type to JSON
• TS type to .NET
public class ParagraphFormattingInfo {[JSONEnum(JSONParagraphFormattingProperty.Alignment)]public ParagraphAlignment Alignment { get; set; }
[JSONEnum(JSONParagraphFormattingProperty.BackColor)]public Color BackColor { get; set; }
[JSONEnum(JSONParagraphFormattingProperty.LeftIndent)]public int LeftIndent { get; set; }
…
public class ParagraphFormattingInfoExporter : IExporter<ParagraphFormattingInfo> { public void FillHashtable(Hashtable result, ParagraphFormattingInfo info) {
result.Add(0, (int)info.Alignment); result.Add(1, info.BackColor.ToArgb());result.Add(2, info.LeftIndent);
…public void RestoreInfo(Hashtable source, ParagraphFormattingInfo info) {
info.Alignment = (ParagraphAlignment)source[0];info.BackColor = Color.FromArgb(source[1]);info.LeftIndent = Convert.ToInt32(source[2]);
…
18Go
# C
on
fere
nce
s
Асинхронное выполнение длительных функций
Длительные операции выполняются «асинхронно»
Цикличные алгоритмы упаковываются в вызовы множества
«быстрых» примитивных функций
Быстрые функции вызываются поочередно из setTimeout
функции с нулевым интерваломrunFormating() {
if(this.timerID)return;
var asyncCalculating = () => {if(this.formatter.formatNext())
this.timerID = setTimeout(asyncCalculating, 0);else
this.timerID = null;};this.timerID = setTimeout(asyncCalculating, 0);
}
runFormatting() {while(this.formatter.formatNext()) { }
}
19
Синхронизация с сервером
Модель стейтов и реквестов
Синхронизация изменений от нескольких пользователей
20Go
# C
on
fere
nce
s
Сессионная модель сервера
Реквесты делаются к HttpHandler, но не к странице
Инстанс пользовательской сессии хранится в статичном
Dictionary. Ключ отсылается на клиент
Сессия идентифицирует конкретную модель (документ), а
не пользователя
Пользователя идентифицирует его ClientGuid (создается
на открытии страницы)
Несколько человек могут открыть один и тот же документ
и работать с одной сессией
Клиент
• Тело реквеста
• ID рабочей сессии
• ID клиента
HttpHandler• Поиск рабочей сессии по ID
• Передача ей ID клиента и
тела реквеста
Сессия
• Обработка реквеста
• Формирование
ответа
21Go
# C
on
fere
nce
s
Очередь реквестов
Реквесты на сервер не блокируют UI контрола
Формирование очереди реквестов
Сформированные реквесты добавляются очередь
Очередь накапливается в течение N секунд
Защита от коллизий
Один и тот же реквест может быть отправлен несколько раз,
пока сервер не подтвердит его принятие
Каждый реквест помечен своим идентификатором (версией
документа)
Сервер не выполнит реквест с одним и тем же ID дважды
22Go
# C
on
fere
nce
s
Путь к collaboration
Две роли клиентов – Editor и Viewer
Роли клиентов распределяет сервер
Editor – клиент, последний изменивший актуальную
модель
Viewer может стать редактором, если у него была
последняя версия модели и он ее поменял
Viewer с устаревшей моделью – менять ее не может
Рассинхронизация возможна только на коротком
интервале времени или при потере связи
23
Integration тестирование
Как протестировать корректную инициализацию клиента
Тестирование синхронизации клиента и сервера
PhantomJS
24Go
# C
on
fere
nce
s
Клиентская часть повторяет серверную
Server-side
Client-side
25Go
# C
on
fere
nce
s
Тестируем клиент и сервер одновременно
Инициализация стартового состояния
Тестирование применения изменений клиента на
сервере
Тестирование применения изменений состояния
серверной модели к клиентской модели
Тестирование возможных коллизий при синхронизации
состояний
26Go
# C
on
fere
nce
s
Цикл интеграционного тестирования
Серверный код теста
Assert полученного состояния
клиентской модели
Применение клиентского
JSON к серверной модели
Assert нового состояния
серверной модели
PhantomJS с клиентским инстансом
Чтение инициализационного
JSON
Выполнение клиентских
операций
Запись в console клиентского
состояния модели
Серверный код теста
Создание серверного инстанса Заполнение модели
27Go
# C
on
fere
nce
s
Пример интеграционного теста
[Test]
public void TestParagraphProperties() {
ChangeDocumentModel(); // предварительная настройка серверной модели
string[] clientResults = RunClientSide(
GetClientModelStateAction(), // записать Model в Output
ExecuteClientCommandAction() // выполнить клиентскую команду, записать реквест в Output
);
// clientResults[0] – JSON с клиентским состоянием модели (инициализация)
// clientResults[1] – JSON с реквестом на изменение серверной модели
AssertClientModelState(clientResults[0]);
ApplyClientRequestToServerModel(clientResults[1]);
AssertServerModelState(DocumentModel);
}
28Go
# C
on
fere
nce
s
Выбор инструментовСинхронизация и
интеграционное тестированиеМиграция кода на клиент
TypeScript – отличный
инструмент для сложных
клиентских проектов
Jasmine+Chutzpah –
покрывают все задачи по
тестированию клиентского
кода
PhantomJS – помогает
написать интеграционные
тесты
Для переноса серверных
типов на клиент можно
использовать TextTemplates
Сложные вычисления можно
разделить на атомарные
операции и выполнять
очередью из таймаутов
Строгая типизация TypeScript
позволяет довольно просто
портировать логику на клиент
Реквесты на сервер не
должны блокировать UI
Контрол не должен зависеть
от ответов сервера
Интеграционные тесты
позволяют протестировать
стартовую инициализацию
Применение клиентских
изменений на сервере также
покрывается тестами
ASPxRichEdit – «толстые» клиент и сервер
29Go
# C
on
fere
nce
s
Синхронизация моделей без коллизий
30Go
# C
on
fere
nce
s
Спасибо за внимание!
Продукты DevExpress можно попробовать на нашем
стенде и на http://devexpress.com
Ведущий разработчик команды ASP.NET
Решетников Роман
Developer Express Inc.