``

Новый подход

к разработке

WEB-проектов и сервисов для мобильных устройств.

Версия: 1.329.03.2015

Что такое Incoding Framework?

Incoding Framework - open source библиотека для быстрой разработки web/desktop-приложений, которая обеспечивает решение многих задач - Incoding Framework используется на всех этапах разработки приложений, что позволяет Вам писать меньше кода и сфокусироваться на бизнес-логике и правилах Вашего проекта:

Кратко о CQRS

Проблемой при разработке бизнес-приложений является "связанность" компонентов, что критично в сложных сценариях, поэтому в качестве основы серверной архитектуры был выбран CQRS, который позволяет достичь высокого уровня декомпозиции. Основными особенностями реализации Incoding Framework является то, что он содержит набор базовых классов и интерфейсов, которые покрывают большинство популярных сценариев разработки.

Нажать на зеленое выделение чтобы увидеть более подробную информацию о элементе ...

Command – используйте для работы с базой данных (транзакции) или для выполнения других задач (без открытия транзакций)
public classs AddCustomerCommand : CommandBase { public string First { get; set; } public string Last { get; set; } public string Mark { get; set; } public override void Execute() { Repository.Save(new Customer { First = First, Last = Last, Mark = Mark }); } }
* Command - первая из двух атомарных частей CQRS, отвечающая за Write-функционал бизнес-логики приложения. При помощи Command вы можете выполнить какую-либо операцию с базой данных (сохранение сущности Customer из примера) или выполнить какое-либо другое не связанное с базой данных действие. Все Command наследуются от базового класса CommandBase.
Query – получайте данные из БД (с открытием read-uncommited–транзакции) или из внешних источников данных
public classs GetCustomersQuery : QueryBase<List<GetCustomersQuery.Response>> { public string Search { get; set; } public classs Response { public string FullName { get; set; } public string Id { get; set; } public string Date { get; set; } } protected override List<Response> ExecuteResult() { return Repository.Query(whereSpecification: new CustomerBySearchWhereSpec(Search)) .ToList() .Select(r => new Response { Id = r.Id.ToString(), FullName = r.First + r.Last, Date = r.CreateDt.ToShortDateString() }) .ToList(); } }
* Query - вторая из двух атомарных частей CQRS, которая отвечает за Read-функционал бизнес-логики приложения. При помощи Query вы можете получить данные из любого ресурса будь то БД (получение отфильтрованной по полям First и Last коллекции сущностей Customer из примера) или какое-либо другое хранилище данных. Все Query наследуются от базового класса QueryBase.
Dispatcher – выполняйте ваши Command и Query, соблюдая паттерн UnitOfWork
public classs CustomerController : IncControllerBase { public ActionResult Fetch(GetCustomersQuery query) { return IncJson(query); } public ActionResult Add(AddCustomerCommand input) { return TryPush(input); } }
* Dispatcher - это ключевой элемент в работе CQRS, который выполняет Command и Query в рамках одной транзакции, реализуя паттерн Unit Of Work, а также позволяет воспользоваться всеми преимуществами AOP без дополнительных плагинов или сторонних библиотек. По умолчанию используется DispatcherDefault реализация.
Read more:

Немного о IML

Динамическая составляющая языка JаvaScript плохо сказывается на поддержке приложений, а также требует от разработчика постоянно учитывать все тонкости динамической типизации (null, undefined, ‘undefined‘, this). IML представляет собой метаязык, позволяющий создавать клиентские сценарии без написания JavaScript-кода - вы можете построить клиентские сценарии своего приложения, используя привычный синтаксис C#.

IML Action – пишите собственные обработчики для стандартных событий браузера
@model AddCustomerCommand @(Html.When(JqueryBind.Click) .Direct() .OnSuccess(dsl => dsl.WithId("customers").Core().Trigger.Incoding()) .AsHtmlAttributes() .ToButton("Refresh")) @(Html.When(JqueryBind.InitIncoding) .AjaxGet(Url.Dispatcher().Query(new GetCustomersQuery()).AsJson()) .OnSuccess(dsl => { string urlToTmpl = Url.Dispatcher().AsView("~/Views/Customer/Table_Tmpl.cshtml"); dsl.Self().Core().Insert.WithTemplateByUrl(urlToTmpl).Html(); }) .AsHtmlAttributes() .ToDiv()) @Html.TextBoxFor(r => r.First) @Html.TextBoxFor(r => r.Last) @Html.TextBoxFor(r => r.Mark) @(Html.When(JqueryBind.Click) .AjaxPost(Url.Dispatcher().Push(new AddCustomerCommand { First = Html.Selector().Name(r => r.First), Last = Html.Selector().Name(r => r.Last) })) .OnSuccess(dsl => dsl.WithId("customers").Core().Trigger.Incoding()) .AsHtmlAttributes() .ToLink("Add"))
* IML позволяет создавать собственные обработчики стандартных событий браузера (Click, PressKey, FocusIn, OnLoad == InitIncoding и пр.) без написания JavaScript-кода. В процессе обработки события можно выполнять Ajax-запросы на сервер (Ajax(), AjaxPost(), AjaxGet() ), причем возможна автоматическая работа с HashQueryString по имени параметров (AjaxHash(), AjaxHashGet(), AjaxHashPost() ). Если необходимости в запросах нет, то можно использовать прямую обработку события Direct(). Во всех из перечисленных случаев также можно отменить обработку события браузером по умолчанию (PreventDefault() ) и/или остановить дальнейшее распространение события (StopPropagation() ).
Read more:

Зачем MVD

При работе с CQRS все действия бизнес-логики делятся на команды (Command) и запросы (Query). Код приложения заметно упрощается, однако при использовании ASP.NET MVC сильно возрастает количество тривиальных action'ов для выполнения команд или запросов. В Incoding Framework для решения этой проблемы используется паттерн Model View Dispatcher, который позволяет выполнять команды и запросы без написания action'ов.

DispatcherControllerBase – используйте, чтобы соблюдать принцип DRY и не писать тривиальные action'ы
public classs DispatcherController : DispatcherControllerBase { public DispatcherController() : base(typeof(Bootstrapper).Assembly) { } }
* MVD покрывает большинство сценариев, которые встречаются при веб-разработке на платформе ASP.NET MVC. При этом, если возникнет необходимость, также остается возможность использования Attributes - вместо пометки атрибутами action'а будет выполняться пометка соответствующего запроса или соответствующей команды.
Read more:

Юнит-тесты

Польза использования модульных тестов (юнит-тестирование) доказывалась множеством различных ресурсов и авторов. Модульные тесты существенно упрощают поддержку проектов и помогают избежать множества ошибок при разработке. В Incoding Framework в качестве оболочки для быстрого написания качественных юнит-тестов используется Mock message.

Persistence test – тестируйте ваши сущности всего несколькими строками кода
[Subject(typeof(Customer))] public classs When_save_Customer : SpecWithPersistenceSpecification<Customer> { It should_be_verify = () => persistenceSpecification.VerifyMappingAndSchema(); }
* Тесты для сущностей работают с тестовой базой данных: создается экземпляр сущности с автоматически заполненными полями, он сохраняется в тестовую базу данных, а затем извлекается по ключу и сверяется с изначально созданным экземпляром.
Specification test – проверяйте правильность фильтрации данных вашими WhereSpecification
[Subject(typeof(CustomerBySearchWhereSpec))] public classs When_customer_by_search { Establish establish = () => { Func<string, string, Customer> createEntity = (first, last) => Pleasure.MockStrictAsObject<Customer> (mock => { mock.SetupGet(r => r.First).Returns(first); mock.SetupGet(r => r.Last).Returns(last); }); fakeCollection = Pleasure.ToQueryable(createEntity(Pleasure.Generator.TheSameString(), Pleasure.Generator.TheSameString()), createEntity(Pleasure.Generator.String(), Pleasure.Generator.String())); }; Because of = () => { filterCollection = fakeCollection .Where(new CustomerByNameWhereSpec(Pleasure.Generator.TheSameString()).IsSatisfiedBy()).ToList(); }; It should_be_filter = () => { filterCollection.Count.ShouldEqual(1); filterCollection[0].First.ShouldContain(Pleasure.Generator.TheSameString()); filterCollection[0].Last.ShouldContain(Pleasure.Generator.TheSameString()); }; static IQueryable<Customer> fakeCollection; static List<Customer> filterCollection; }
* При помощи Mock message легко создавать коллекции с автоматическим заполнением полей сущностей для тестирования фильтрующих спецификаций.
Query test - проводите проверку верности работы ваших запросов
[Subject(typeof(GetCustomersQuery))] public classs When_get_customers { Establish establish = () => { var query = Pleasure.Generator.Invent<GetCustomersQuery>(); customer = Pleasure.Generator.Invent<Customer>(); mockQuery = MockQuery<GetCustomersQuery, List<GetCustomersQuery.Response>> .When(query) .StubQuery(whereSpecification: new CustomerBySearchWhereSpec(query.Search), entities: customer); }; Because of = () => mockQuery.Original.Execute(); It should_be_result = () => mockQuery.ShouldBeIsResult(list => list.ShouldEqualWeakEach(new List<Customer> { customer }, (dsl, i) => dsl.ForwardToValue(r => r.Date, customer.CreateDt.ToShortDateString()) .ForwardToValue(r => r.FullName, customer.First + customer.Last) .ForwardToValue(r => r.Id, customer.Id.ToString()))); static MockMessage<GetCustomersQuery, List<GetCustomersQuery.Response>> mockQuery; static Customer customer; }
* Mock message работает с Repository, используя mockup (за исключением тестирования сущностей), а не реальные реализации, что позволяет проводить тестирование вне контекста базы данных. При проведении проверки ShouldEqualWeakEach() проводит сверку полей коллекций автоматически, но также имеется возможность использовать методы Forward(), ForwardToValue(), ForwardToAction() и пр. для более тонкой настройки.
Command test - следите за правильностью исполнения ваших команд
[Subject(typeof(AddCustomerCommand))] public classs When_add_customer { static MockMessage<AddCustomerCommand, object> mockCommand; Establish establish = () => { AddCustomerCommand command = Pleasure.Generator.Invent<AddCustomerCommand>(); mockCommand = MockCommand<AddCustomerCommand> .When(command); }; Because of = () => mockCommand.Original.Execute(); It should_be_saved = () => mockCommand.ShouldBeSave<Customer>(customer => customer.ShouldEqualWeak(mockCommand.Original)); }
* Тестирование комманд немногим отличается от тестирования запросов, так как QueryBase<TResult> и CommandBase реализуют интерфейс IMessageBase<TResult>, разница заключается только в работе с транзакцией.
Read more:

Что-то о Selector'ах

Так как IML - декларативный язык, который описывает сценарий выполнения кода на клиентской строне, Selector'ы имеют огромное значение в рамках Incoding Framework - благодаря им можно получить любые атрибуты DOM-элементов на клиентской стороне приложения в runtime. При использовании селекторов IML автоматически определяет тип элемента и на основе этого решает каким способом получить значение.

Selector.Jquery – получайте любые элементы на DOM в одну строчку
var idSelector = Selector.Jquery.ID("id"); var classSelector = Selector.Jquery.Class("class"); var nameSelector = Selector.Jquery.Name("name");
* Selector.Jquery предоставляет стандартные варианты получения элементов: по Id, по Class, по Name или текущего элемента (Selector.Jquery.Self() - аналог event.target). Также возможно использовать типизированной формы этих селекторов (Html.Selector() - в качестве модели типизации принимает указанную во View model).
Selector.JS и Selector.Incoding – используйте специальные селекторы для специфических задач
var incQueryString = Selector.Incoding.QueryString("key"); var incHashQueryString = Selector.Incoding.HashQueryString("key"); var incHashUrl = Selector.Incoding.HashUrl("key"); var incCookie = Selector.Incoding.Cookie("key"); var incAjax = Selector.Incoding.Ajax(options => { options.Async = true; options.Type = HttpVerbs.Get; options.Url = "url-to-get"; options.Cache = true; }); var jsDateTime = Selector.JS.DateTime.GetDate(); var jsLocation = Selector.JS.Location; var jsGeoLocation = Selector.JS.Navigator; var jsFunctionResult = Selector.JS.Eval("MyFunc()");
* Специальные селекторы предоставляют широкий перечень возможностей для построения клиентских сценариев: Selector.Incoding позволяют получать данные, которые хранятся в location, cookies или доступны по запросу на сервер, Selector.JS позволяет получать значения, доступные в JavaScript - DateTime, Document, GeoLocation, а также позволяет выполнять JS-код.
Read more:

Кое-что о Template

В Incoding Framework помимо стандартных Razor-шаблонов, отрабатывающих на сервере, применяются клиентские template для преобразования JSON-данных в html. Дополнительное использование клиентских template позволяет легко использовать вставку JSON-данных через Ajax-запросы, снижают объем трафика за счет передачи не готового тяжеловесного html'я, а компактного JSON'а, а также позволяет повторно использовать запросы при мобильной разработке.

Clien template – формируйте ваш html на основе полученных через Ajax JSON-данных
using (var template = Html.IncodinG().Template<GetCustomersQuery.Response>()) { <table> <thead> <tr> <th>Id</th> <th>NamE</th> <th>Mark</th> </tr> </thead> @using (var each = template.ForEach()) { <tr> <td>@each.For(r => r.Id)</td> <td>@each.For(r => r.FullName)</td> <td>@each.For(r => r.Mark)</td> </tr> } </table> }
* При построении template, надо указывать только тип модели - при этом не учитывается коллекция это или один объект. Некоторые template engine требуют указывать, что конкретно будет – коллекция или один объект. В Incoding Framework было решено убрать данное условие и при построение template не учитывать сколько будет элементов. Благодаря этой особенности нет необходимости в методе With, который поддерживают не все template engine.
Read more:

Some projects built using Framework

RAPNDA PROJECT

ReAdmissionProgram and WellBeingDirect software

Vestibulum at mollis neque. Pellentesque tempus, urna sit amet condimentum lacinia, ante justo sodales velit, facilisis commodo orci ipsum in sapien. Donec tellus erat, finibus id nunc eu, vehicula molestie neque. Maecenas viverra semper pellentesque.

About TimeNDA PROJECT

Vestibulum at mollis neque. Pellentesque tempus, urna sit amet condimentum lacinia, ante justo sodales velit, facilisis commodo orci ipsum in sapien.

Pazar3.mkView the project

Vestibulum at mollis neque. Pellentesque tempus, urna sit amet condimentum lacinia, ante justo sodales velit, facilisis commodo orci ipsum in sapien. Donec tellus erat, finibus id nunc eu, vehicula molestie neque. Maecenas viverra semper pellentesque.

CTRNDA PROJECT

Calculating Teachers Rating

Vestibulum at mollis neque. Pellentesque tempus, urna sit amet condimentum lacinia, ante justo sodales velit, facilisis commodo orci ipsum in sapien. Donec tellus erat, finibus id nunc eu, vehicula molestie neque. Maecenas viverra semper pellentesque.

eDatingView the project

Vestibulum at mollis neque. Pellentesque tempus, urna sit amet condimentum lacinia, ante justo sodales velit, facilisis commodo orci ipsum in sapien. Donec tellus erat, finibus id nunc eu, vehicula molestie neque.

Specification – where, order, paginated для описания вашего запроса к базе данных public classs CustomerBySearchWhereSpec : Specification<Customer> { readonly string _search; public CustomerBySearchWhereSpec(string search) { this._search = search; } public override Expression<Func<Customer, bool>> IsSatisfiedBy() { if (string.IsNotOrWhiteSpace(this._search)) return null; return customer => customer.Last.Contains(this._search) || customer.First.Contains(this._search); } } Query – получайте данные из БД (с открытием read-uncommited–транзакции) или из внешних источников данных public classs GetCustomersQuery : QueryBase<List<GetCustomersQuery.Response>> { public string Search { get; set; } public classs Response { public string FullName { get; set; } public string Id { get; set; } public string Date { get; set; } } protected override List<Response> ExecuteResult() { return Repository.Query(whereSpecification: new CustomerBySearchWhereSpec(Search)) .ToList() .Select(r => new Response { Id = r.Id.ToString(), FullName = r.First + r.Last, Date = r.CreateDt.ToShortDateString() }) .ToList(); } } Command – используйте для работы с базой данных (транзакции) или для выполнения других задач (без открытия транзакций) public classs AddCustomerCommand : CommandBase { public string First { get; set; } public string Last { get; set; } public override void Execute() { Repository.Save(new Customer { First = First, Last = Last, CreateDt = DateTime.UtcNow }); } } Dispatcher – выполняйте ваши Command и Query, соблюдая паттерн UnitOfWork if (!ModelState.IsValid) return IncodingResult.Error(ModelState); try { dispatcher.Push(input); return IncodingResult.Success(); } catch (IncWebException incWebException) { foreach (var error in incWebException.Errors) { foreach (var message in error.Value) ModelState.AddModelError(error.Key, message); } return IncodingResult.Error(ModelState); }