disclamer:this article is a step-by-step guide to help you to familiarize with the core functionality of Incoding Framework. Following the guide will result in an application that implements the work with the DB (CRUD + data filters) and fully covered with unit tests.

Part 0. Introduction

Let us begin with a short description of Framework. Incoding Framework comprises three packages: Incoding framework – back-end project, Incoding Meta Language – front-end project and Incoding tests helpers – unit-tests for back-end. These packages are installed independently of each other, making it possible to integrate framework by parts into the project: You can connect only front or back end (tests are tightly coupled with the back end, so, they could be more considered as a complement).

Projects developed in Incoding Framework,use CQRS as a server architecture. Incoding Meta Language. В целом Incoding Framework is used as a basic tool for building front-end. All in all, Incoding Framework covers the entire application development cycle.

Typical solution, that was developed using Incoding Framework, comprises 3 projects:

1. Domain (class library) – is responsible for business logic and database operations.

UI (ASP.NET MVC project)– front-end based on ASP.NET MVC.

UnitTests (class library) – unit-tests for Domain.

Domain

After installation of Incoding framework through Nuget , along with the necessary dll, Bootstrapper.cs file will be added in the project. The file is mainly responsible for the initialization of an application: logging initialization, IoC registration, installation of Ajax-requests settings, etc. By default, StructureMapis installed as loC framework, but there is a provider for Ninject, and it is also possible to write your own implementations.

Part 1. Installation.

So, we proceed to the task of the disclamer and start writing our application. The first phase of building the application is to create solution structure of a project and to add the projects to it. The project solution will be called Example and, as was already mentioned in the introduction, will have 3 projects. We begin with the project that is responsible for business logic of the application – Domain.

Create class library Domain.

Then we proceed to the front-end – create and install ASP.NET Web Application UI with links to the MVC packages as template, empty project.

Finally, we add class library UnitTests that is responsible for unit testing.

Note: Although UnitTests are not an obligatory part of the application, we recommend you to cover the code with tests as it will help to avoid numerous problems in future with various possible faults in the code due to test automation.

After having finished all the above activities, you will get following solution:

After we create the solution structure, we need to install Incoding Framework package from Nuget.

The installation carried out by Nuget. There is the same algorithm of installation for all the projects:

Right-click the project and select Manage Nuget Packages… in the context menu

Part 3. CRUD.

After the actions described above, we come to the most interesting part – code writing implementing the CRUD (create, read, update, delete) functionality of an application. To begin this process, create an entity class that will map to the DB. In our case, this is Human.cs that we add to the Example.Domain -> Persistences folder.

Human.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

namespaceExample.Domain

{

#region << Using >>

usingSystem;

usingIncoding.Data;

#endregion

publicclassHuman:IncEntityBase

{

#region Properties

publicvirtualDateTimeBirthday{get;set;}

publicvirtualstringFirstName{get;set;}

publicvirtualstringId{get;set;}

publicvirtualstringLastName{get;set;}

publicvirtualSexSex{get;set;}

#endregion

#region Nested Classes

publicclassMap:NHibernateEntityMap<Human>

{

#region Constructors

protectedMap()

{

IdGenerateByGuid(r=>r.Id);

MapEscaping(r=>r.FirstName);

MapEscaping(r=>r.LastName);

MapEscaping(r=>r.Birthday);

MapEscaping(r=>r.Sex);

}

#endregion

}

#endregion

}

publicenumSex

{

Male=1,

Female=2

}

}

Our class contains several fields where we will write data and Nested Class Map.

Note: after creating the Human class, you do not need to perform any operations (creating an XML mapping) due to FluentNhibernate.

We can now add commands and queries, which are responsible for realization of the CRUD operations. The first command will be responsible for adding a new or change an existing record of the Human type. The command is quite simple: we either get an entity on a Repository using the key (ld) or, if no entity exist, we create a new one. Both of these entities get the values specified in the properties of the AddOrEditHumanCommand class. Add Example.Domain -> Operations -> AddOrEditHumanCommand.cs to the project.

AddOrEditHumanCommand.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

namespaceExample.Domain

{

#region << Using >>

usingSystem;

usingFluentValidation;

usingIncoding.CQRS;

usingIncoding.Extensions;

#endregion

publicclassAddOrEditHumanCommand:CommandBase

{

#region Properties

publicDateTimeBirthDay{get;set;}

publicstringFirstName{get;set;}

publicstringId{get;set;}

publicstringLastName{get;set;}

publicSexSex{get;set;}

#endregion

publicoverridevoidExecute()

{

varhuman=Repository.GetById<Human>(Id)??newHuman();

human.FirstName=FirstName;

human.LastName=LastName;

human.Birthday=BirthDay;

human.Sex=Sex;

Repository.SaveOrUpdate(human);

}

}

}

The Read command is the second part of the CRUD. This is a request for reading entities from the DB. Add the file Example.Domain -> Operations -> GetPeopleQuery.cs.

GetPeopleQuery.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

namespaceExample.Domain

{

#region << Using >>

usingSystem.Collections.Generic;

usingSystem.Linq;

usingIncoding.CQRS;

#endregion

publicclassGetPeopleQuery:QueryBase<List<GetPeopleQuery.Response>>

{

#region Properties

publicstringKeyword{get;set;}

#endregion

#region Nested Classes

publicclassResponse

{

#region Properties

publicstringBirthday{get;set;}

publicstringFirstName{get;set;}

publicstringId{get;set;}

publicstringLastName{get;set;}

publicstringSex{get;set;}

#endregion

}

#endregion

protectedoverrideList<Response>ExecuteResult()

{

returnRepository.Query<Human>().Select(human=>newResponse

{

Id=human.Id,

Birthday=human.Birthday.ToShortDateString(),

FirstName=human.FirstName,

LastName=human.LastName,

Sex=human.Sex.ToString()

}).ToList();

}

}

}

The Delete command is the remaining part of the CRUD. The command deletes records from the DB using the key (ld). Add the file Example.Domain -> Operations -> DeleteHumanCommand.cs.

DeleteHumanCommand.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

namespaceExample.Domain

{

#region << Using >>

usingIncoding.CQRS;

#endregion

publicclassDeleteHumanCommand:CommandBase

{

#region Properties

publicstringHumanId{get;set;}

#endregion

publicoverridevoidExecute()

{

Repository.Delete<Human>(HumanId);

}

}

}

In order to populate the DB with initial data, add the file Example.Domain -> InitPeople.cs that is derived from the ISetUP interface.

ISetup

1

2

3

4

5

6

7

8

9

10

11

usingSystem;

namespaceIncoding.CQRS

{

publicinterfaceISetUp:IDisposable

{

intGetOrder();

voidExecute();

}

}

All the class instances from the ISetUp are registered with IoC in the Bootstrapper.cs (see Introduction) and run (public void Execute() ) in order (public int GetOrder() ).

InitPeople.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

namespaceExample.Domain

{

#region << Using >>

usingSystem;

usingIncoding.Block.IoC;

usingIncoding.CQRS;

usingNHibernate.Util;

#endregion

publicclassInitPeople:ISetUp

{

publicvoidDispose(){}

publicintGetOrder()

{

return0;

}

publicvoidExecute()

{

//get Dispatcher for execute Query or Command

vardispatcher=IoCFactory.Instance.TryResolve<IDispatcher>();

//don't add new entity if exits

if(dispatcher.Query(newGetEntitiesQuery<Human>()).Any())

return;

//Adding new entity

dispatcher.Push(newAddOrEditHumanCommand

{

FirstName="Hellen",

LastName="Jonson",

BirthDay=Convert.ToDateTime("06/05/1985"),

Sex=Sex.Female

});

dispatcher.Push(newAddOrEditHumanCommand

{

FirstName="John",

LastName="Carlson",

BirthDay=Convert.ToDateTime("06/07/1985"),

Sex=Sex.Male

});

}

}

}

The back-end implementation of the CRUD is ready. Now it is time to add a user code. As in the case of the back end, we begin the implementation with creating/editing a record. Add the file Example.UI -> Views -> Home -> AddOrEditHuman.cshtml.

The IML-code creates the standard HTML form and works with AddOrEditHumanCommand, sending the appropriate Ajax query to the server.

Then comes the template for data loading through the GetPeopleQuery. There is a description of the table that will be responsible not only for data output, but also for record deletion and editing: add the file Example.UI -> Views -> Home -> HumanTmpl.cshtml.

Note:The task of opening a dialog box is quite common, so the code that is responsible for this task can be exported to the extension.

Thus, it remains to change the start page so that during its loading AJAX query is transmitted to the server for obtaining data from the GetPeopleQuery and mapping of data using HumanTmpl: change the file Example.UI -> Views -> Home -> Index.cshtml so that it looks as follows.

In real-world applications, validation of input form data is one of the most frequent task. Therefore, we add data validation on the adding/editing form of the Human entity. First, we need to add a server code. Add the following code in AddOrEditHumanCommand as a nested class:

So we have written the application code that implements the CRUD functionality for one DB entity.

Part 4. Specifications – data cleaning.

Another task that often occurs in real projects is to clean requested data. Incoding Framework uses WhereSpecifications for convenient code writing and complying an encapsulation principle for cleaning data from Query. In the written code add a possibility to clean data from GetPeopleQuery by FirstName and LastName. First, add two specification files Example.Domain -> Specifications -> HumanByFirstNameWhereSpec.cs and Example.UI -> Specifications -> HumanByLastNameWhereSpec.cs

Now use the written specifications in GetPeopleQuery. .Or()/.And() relations allow to merge atomic specifications that helps to use the created specifications many times and fine-tune necessary data filters (in the example we use .Or() relation)

Part 5. Unit-test.

Let’s cover the written code with tests. The first one is responsible for testing of Human entity mapping. Add the file When_save_Human.cs to the folder Persisteces of the UnitTests project.

When_save_Human.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

namespaceExample.UnitTests.Persistences

{

#region << Using >>

usingExample.Domain;

usingIncoding.MSpecContrib;

usingMachine.Specifications;

#endregion

[Subject(typeof(Human))]

publicclassWhen_save_Human:SpecWithPersistenceSpecification

{

#region Fields

It should_be_verify=()=>persistenceSpecification.VerifyMappingAndSchema();

#endregion

}

}

The test works with a test database (Example_test): an instance of the Human class with automatically populated fields is created, then stored in the DB, retrieved from and compared to the created instance.

Then add the tests for WhereSpecifications in a folder named Specifications.

Now we have to add tests for the command and the query (Operations folder). For the command, you need to add two tests: the first one verifies the creation of a new entity; the second one verifies the editing of an existing entity.