Another MVC Application

download Another MVC Application

of 43

Transcript of Another MVC Application

  • 7/24/2019 Another MVC Application

    1/43

    Another MVC Application: Introduction

    All websites are growing faster these days, and once it grows, it is very hard to write,

    organize and maintain. As we add new functionality or developer to a project, any large web

    applications with poor design may be go out of control. So the idea behind this article is to

    design a website architecture that must be simple, easily understandable by any web designer

    (beginner to intermediate) and Search Engines. or this article ! am trying to design a website

    for any individuals to maintain their contact details online. "owever, in future, the same

    application could be used by large community all over the world with added functionality and

    modules. So, the design should be easily adaptable in order to cope with the future growth of

    business.

    !n this article ! will tal# about creating and designing $ser !nterface ($!) in such a manner so

    that $! will be separated from the business logic, and can be created independently by any

    designer%developer. or this part we will use AS&.'et *, +noc#out -uery and ootstrap.later in this article we will discuss more about database design and implementing business

    logic using structured layers using S/l Server 0112, Entity ramewor#, and *astle 3indsor

    for 4ependency !njection.

    Separation of Concern: Primary Objective

    5he #ey concept is stripping out most or all logic. 6ogic should not be bound to a page. 3hat

    if we need to re7use the logic from one page in another8 !n that case we will be tempted to

    copy7and7paste. !f we are doing this then our project will become maintainable. Another

    important concept is to separate data access layer from any business logic, as we are planning

    to use Entity ramewor# this is less of a problem as E should already have this end separate.

    3e should be able to easily move all our E files to another project and simply add a

    reference to the projects that need it. elow is the high level design9

  • 7/24/2019 Another MVC Application

    2/43

    And, the final solution will loo# li#e below image in isual Studio 9

    Seven Projects in One Solution : Is it required

    !t is all about your decision:5he proposed designed will offer some rather relevant

    benefits, which include9

    Separation of *oncern9 4esign should allow clear and defined layers; means

    segregate application into distinct areas whose functionality does not overlap. such

    that $!7designers can focus on their job without dealing with the business logic

    (Application.3eb), and the core developer can only wor# on main business logic

  • 7/24/2019 Another MVC Application

    3/43

    &roductivity9 !t is easier to add new features to e>isting software, since the structure is

    already in place, and the location for every new piece of code is #nown beforehand,

    so any issue can be easily identified and separated to cope with comple>ity, and to

    achieve the re-uired engineering -uality factors such as ? ? robustness, adaptability,

    maintainability, and re7usability.

    aintainability9 !t is easier to maintain application, due to clear and defined structure of the

    code is visible and #nown, so it@s easier to find bugs and anomalies, and modify them with

    minimal ris#.

    Adaptability9 'ew technical features, such a different front ends, or adding a business

    rule engine is easier to achieve, as your software architecture creates a clear

    separation of concerns.

    e7usability9 e7usability is another concern on designing any application, because it is one

    of the main factors to decrease the total cost of ownership, and our design should consider to

    what e>tent we can reuse the created 3eb application and different layers.

    !n last section of this article, we will discuss the functionality of each individual layer@s in

    details.

    !ools " !echnolo#y

    5o achieve the final solution, we need below tools%dll9

    isual Studio 01B0

    AS&.'E5 * C with azor iew Engine

    Entity ramewor# D.1

    *astle 3indsor for 4!

    S/6 Server 0112%01B0

    +noc#out.js /uery

    *astle 3indsor for 4!

    ootstrap *SS

    $hat %e are tryin# to achieve: &equirement

    Screen ': Contact (ist ) Vie% all contacts

    B.B 5his screen should display all the contacts available in 4atabase.

    B.0 $ser should be able to delete any contact.B.F $ser should able to edit any contact details.

  • 7/24/2019 Another MVC Application

    4/43

    B.C $ser should be able to create a new contact.

    !nitial s#etch9

    Screen *: Create +e% Contact

    5his screen should display one blan# screen to provide functionality as.

    0.B $ser should be able to Enter his%her irst name, 6ast 'ame and Email Address.

    0.0 $ser should able to add any number of &hone numbers by clic#ing on Add numbers.

    0.F $ser should able to remove any phone number.

    0.C $ser should able to add any number of Addresses by clic#ing on Add new address.0.D $ser should able to remove any address.

    0.G *lic# on save button should save *ontact details in 4atabase and user will return bac# in

    *ontact 6ist page.

    0.H *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.

  • 7/24/2019 Another MVC Application

    5/43

    !nitial s#etch9

    Screen ,: -pdate ./istin# Contact

    5his screen should display screen with selected contact information details.

    F.B $ser should be able to modify his%her irst name, 6ast 'ame and Email Address.

    F.0 $ser should able to modify %delete%Add any number of &hone numbers by clic#ing on

    Add numbers or delete lin#.

    F.F $ser should able to modify %delete%Add any number of Addresses by clic#ing on Add new

    address or delete lin#.

    F.C *lic# on save button should update *ontact details in 4atabase and user will return bac#

    in *ontact 6ist page.

    F.D *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.

  • 7/24/2019 Another MVC Application

    6/43

    !nitial S#etch9

    Part ': Create $eb Application 01noc2out3js4 Asp3+etMVC and 5ootstrap6: 7or 8esi#ners

    efore #ic#7off the $! part, let us see what benefits we are getting using +noc#outjs and

    ootstrap along with AS&.'E5 * C.

    $hy 1noc2outjs:+noc#out is an pattern that wor#s with a javascript iewodel.

    5he reason this wor#s well with * is that serialization to and from javascript models in

    S=' is very simple, and it is also included with * C. !t allows us to develop rich $!ible "56, *SS, and avascript for

    popular user interface components and interactions. !t comes with bundles of *SS styles,

    *omponents and avascript plugins. !t provides *ross platform support, which eliminates

    major layout inconsistencies. Everything just wor#sI Jood documentation and the 5witter

    ootstrapample. And, finally, it saves

    me plenty of times, as it cut the development time to half with very less testing and almost

    zero browser issues. Some other benefits we can get by using this framewor# are9

    B07*olumn Jrid, fi>ed layout, fluid or responsive layout.

  • 7/24/2019 Another MVC Application

    7/43

    ase *SS for 5ypography, code (synta> highlighting with Joogle prettify), 5ables,

    orms, uttons and uses !cons by Jlpyhicons .

    3eb $! *omponents li#e uttons, 'avigation menu, 6abels, 5humbnails, Alerts,

    &rogress bars and misc.

    avascript plugins for odal, 4ropdown, Scrollspy, 5ab, 5ooltip, &opover, Alert,

    utton, *ollapse, *arousel and 5ypehead.

    !n below steps, we will wor# through the layout and design to build $! for above re-uirement

    by using dummy javascript data.

    Step ':

    *reate a new project as lan# Solution; name it as KApplicationL

    Step *:

    ight *lic# on Solution folder and add new &roject of type AS&.'E5 * C as an !nternet

    Application 5emplate with iew engine as azor.

  • 7/24/2019 Another MVC Application

    8/43

  • 7/24/2019 Another MVC Application

    9/43

    After Step 0 7 the project structure should loo# li#e the below image

    Step ,:

    ight clic# on eferences and clic# on anage 'uJet &ac#ages. 5ype ootstrap on search

    bar then clic# on !nstall button.

    Step 9:

  • 7/24/2019 Another MVC Application

    10/43

    add below line of code into undle*onfig.cs file under AppMStart folder to add +noc#outjs

    and ootstrap for every page

    bundles.Add(new ScriptBundle("~/bundles/knockout").Include(

    "~/Scripts/knockout-{version}.js"))

    bundles.Add(new

    St!leBundle("~/ontent/css").Include("~/ontent/bootstr#p.css"))

    Also in M6ayout,cshtml file under iews%Shared folder add below line to register #noc#out

    files as 9

    $Scripts.%ender("~/bundles/knockout")

    Step :

    Add a new folder name as *ontact inside iews, and then add !nde>.cshtml as new iewpage. 5hen add a new *ontroller name it *ontact*ontroller.cs inside *ontroller folder, and

    add a new *ontact.js file under Scripts folder. efer to below image.

  • 7/24/2019 Another MVC Application

    11/43

    Step ;:

    inally modify the default map route in oute.config to point to *ontact controller as9

    routes.p%oute(

    n#'e "e*#ult"+ url "{controller}/{#ction}/{id}"+

    de*#ults new { controller , "ont#ct"+ #ction , "Inde"+

    id , rl#r#'eter.0ption#l }

    )

    And also modify the M6ayout.cshtml file inside iew%Shared as per the ootStrap Synta>.

    elow is the modified code9120345 6t'l7

    16t'l l#n8,"en"7

    16e#d7

    1'et# c6#rset,"ut*-9" /7

    1title7$:iewB#8.3itle - ont#ct '#n#8er1/title7 1link 6re*,"~/*#vicon.ico" rel,"s6ortcut icon" t!pe,"i'#8e/-icon" /7

    1'et# n#'e,"viewport" content,"widt6,device-widt6" /7

    $Scripts.%ender("~/bundles/j;uer!")

    $Scripts.%ender("~/bundles/knockout")

    $St!les.%ender("~/ontent/css")

    $Scripts.%ender("~/bundles/'oderni

  • 7/24/2019 Another MVC Application

    12/43

    'ow we are done with initial setup to run the application. 5he output is as below9

    3e will use this page to display the re-uirement for Screen B i.e. *ontact 6ist 7 iew all

    contacts

    Step =:

    irst we will create a dummy profile data as array in *ontact.js (later we will fetch it from

    database), and then we will use this data to populate Jrid.

    v#r u''!ro*ile , @

    {

    "ro*ileId" +

    "irst?#'e" "An#nd"+

    "C#st?#'e" "#nde!"+

    "5'#il" "#n#nd$#n#ndp#nde!.co'"

    }+ {

    "ro*ileId" D+

    "irst?#'e" "Eo6n"+

    "C#st?#'e" "en#"+

    "5'#il" "jo6n$cen#.co'"

    }

    F

    'e>t, we will create &rofilesiewodel, a viewmodel class which hold &rofiles, an array

    holding an initial collection of 4ummy&rofile data. 'ote that it

  • 7/24/2019 Another MVC Application

    13/43

    Step >:

    'e>t we will write code in !nde>.cshtml page, that

  • 7/24/2019 Another MVC Application

    14/43

    Add one button for *reate new profile and bind it with clic# event using create&rofile

    function.

    So out final code for !nde>.cshtml is 9

    1input t!pe,"button" cl#ss,"btn btn-s'#ll btn-pri'#r!" v#lue,"?ewont#ct" d#t#-bind,"clickGroot.cre#tero*ile" /7

    16r /7

    1t#ble cl#ss,"t#ble t#ble-striped t#ble-bordered t#ble-condensed"7

    1tr7

    1t67irst ?#'e1/t67

    1t67C#st ?#'e1/t67

    1t675'#il1/t67

    1t671/t67

    1/tr7

    1tbod! d#t#-bind,"*ore#c6 ro*iles"7

    1tr7

    1td cl#ss,"n#'e"71# d#t#-bind,"tet irst?#'e+ click

    Gp#rent.editro*ile"71/#71/td7 1td d#t#-bind,"tet C#st?#'e"71/td7

    1td d#t#-bind,"tet 5'#il"71/td7

    1td71button cl#ss,"btn btn-'ini btn-d#n8er" d#t#-bind,"click

    Gp#rent.re'overo*ile"7re'ove1/button71/td7

    1/tr7

    1/tbod!7

    1/t#ble7

    1script src,"~/Scripts/ont#ct.js"71/script7

    And output is9

    'one of the added button and lin# will wor#, because we have not written any code for that,

    so let that in ne>t step.

    Step '':

    Add events for create&rofile, edit&rofile and remove&rofile in *ontact.js

    sel*.cre#tero*ile , *unction () {

    #lert("re#te # new pro*ile")

    }

    sel*.editro*ile , *unction (pro*ile) {

  • 7/24/2019 Another MVC Application

    15/43

    #lert("5dit tis pro*ile wit6 pro*ile id #s " H pro*ile.ro*ileId)

    }

    sel*.re'overo*ile , *unction (pro*ile) {

    i* (con*ir'("Are !ou sure !ou w#nt to delete t6is pro*ile")) {

    sel*.ro*iles.re'ove(pro*ile)

    } }

    'ow when we run our application and clic# on emove button, then it will remove that

    profile from current array. As we define this array as observable, the $! will be #ept in sync

    with changes to that array. 5ry it by clic#ing on emove button. Edit lin# and *reate &rofile

    button will simply display the alert. So, let us implemented this functionality in ne>t steps9

    Step '*:

    'e>t we will add9

    A new azor iew inside iews%*ontact as *reateEdit.cshtml, and register it in

    *ontact*ontroller.cs class.

    public Action%esult re#te5dit()

    {

    return :iew()

    }

    A new js file named as *reateEdit.js inside Scripts folder.

    odify create&rofile and edit&rofile method of *ontact.js, so that it will point to the

    *reateEdit page.

    sel*.cre#tero*ile , *unction () {

    window.loc#tion.6re* , J/ont#ct/re#te5dit/KJ

    }

    sel*.editro*ile , *unction (pro*ile) {

    window.loc#tion.6re* , J/ont#ct/re#te5dit/J H pro*ile.ro*ileId

    }

    unning the application will now wor# for all the events in *ontact 6ist (screen 7B). And

    create and Edit should redirect to *reateEdit page with re-uired parameters.

    Step ',:

    irst we will start with adding &rofile !nformation to this *reateEdit page. or that we need

    to do9

    3e need to get profile!d from url so, add below two lines on top of the *reateEdit.js page

    v#r url , window.loc#tion.p#t6n#'e

  • 7/24/2019 Another MVC Application

    16/43

    v#r pro*ileId , url.substrin8(url.l#stInde0*(J/J) H )

    e*ine # du''! ro*ile d#t# #s #rr#! in re#te5dit.js

    oll#pse L op! ode

    v#r u''!ro*ile , @

    {

    "ro*ileId" +

    "irst?#'e" "An#nd"+ "C#st?#'e" "#nde!"+

    "5'#il" "#n#nd$#n#ndp#nde!.co'"

    }+

    {

    "ro*ileId" D+

    "irst?#'e" "Eo6n"+

    "C#st?#'e" "en#"+

    "5'#il" "jo6n$cen#.co'"

    }

    F

    &rofile, a simple avaScript class constructor that stores a profile@s irst'ame, 6ast'ame and

    Email selection.

    v#r ro*ile , *unction (pro*ile) {

    v#r sel* , t6is

    sel*.ro*ileId , ko.observ#ble(pro*ile pro*ile.ro*ileId K)

    sel*.irst?#'e , ko.observ#ble(pro*ile pro*ile.irst?#'e JJ)

    sel*.C#st?#'e , ko.observ#ble(pro*ile pro*ile.C#st?#'e JJ)

    sel*.5'#il , ko.observ#ble(pro*ile pro*ile.5'#il JJ)

    }

    &rofile*ollection, a viewmodel class that holds profile (a avaScript object providing &rofiledata) along with binding for save&rofile and bac#5o&rofile6ist events.

    v#r ro*ileollection , *unction () {

    v#r sel* , t6is

    //i* ro*ileId is K+ It 'e#ns re#te new ro*ile

    i* (pro*ileId ,, K) {

    sel*.pro*ile , ko.observ#ble(new ro*ile())

    }

    else {

    v#r currentro*ile , G.8rep(u''!ro*ile+ *unction (e) { return

    e.ro*ileId ,, pro*ileId })

    sel*.pro*ile , ko.observ#ble(new ro*ile(currentro*ile@KF)) }

    sel*.b#ck3oro*ileCist , *unction () { window.loc#tion.6re* ,

    J/cont#ctJ }

    sel*.s#vero*ile , *unction () {

    #lert("#te to s#ve is " H

    ES0?.strin8i*!(ko.toES(sel*.pro*ile())))

    }

    }

    And finally, activate +noc#out using #o.applyindings().

  • 7/24/2019 Another MVC Application

    17/43

    ko.#ppl!Bindin8s(new ro*ileollection())

    Step '9:

    'e>t we will write code in *reateEdit.cshtml page, thatisting record with profile !d B9

  • 7/24/2019 Another MVC Application

    18/43

    $pdate any e>isting record and clic# on save with give below output9

    As per re-uirement for this screen, we already have done9

    0.B $ser should be able to Enter his%her irst name, 6ast 'ame and Email Address

    0.G *lic# on save button should save *ontact details in 4atabase and user will return bac# in

    *ontact 6ist page.

    0.H *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.

    !n ne>t step we will try to achieve below re-uirement9

    0.0 $ser should able to add any number of &hone numbers by clic#ing on Add numbers.

    0.F $ser should able to remove any phone number

    Step ':

    5o achieve re-uirement no. 0.0 and 0.F, we need to do9

    4efine a dummy &hone5ype and &hone45= data as an array in *reateEdit.js.

    phone5ype4ata will be used to bind dropdown to define the &hone 5ype for a particular

    &hone number.

    v#r p6one3!pe#t# , @

    {

    "6one3!peId" +

  • 7/24/2019 Another MVC Application

    19/43

    "?#'e" "Mork 6one"

    }+

    {

    "6one3!peId" D+

    "?#'e" "erson#l 6one"

    }

    Fv#r 6one30 , @

    {

    "6oneId"+

    "6one3!peId" +

    "ro*ileId"+

    "?u'ber" "-DDD-===="

    }+

    {

    "6oneId" D+

    "6one3!peId" D+

    "ro*ileId" +

    "?u'ber" "NNN-OOO-PPPP" }

    F

    &rofile, a simple avaScript class constructor that stores a &hone6ine information which

    include its &hone 5ype i.e. whether its K3or# &honeL or K"ome &honeL etc. along with phone

    number.

    v#r 6oneCine , *unction (p6one) {

    v#r sel* , t6is

    sel*.6oneId , ko.observ#ble(p6one p6one.6oneId K)

    sel*.6one3!peId , ko.observ#ble(p6one p6one.6one3!peId K)

    sel*.?u'ber , ko.observ#ble(p6one p6one.?u'ber JJ)}

    odify &rofile*ollection viewmodel class to also holds phone'umbers along with binding

    for add&hone and remove&hone events.

    v#r ro*ileollection , *unction () {

    v#r sel* , t6is

    //i* ro*ileId is K+ It 'e#ns re#te new ro*ile

    i* (pro*ileId ,, K) {

    sel*.pro*ile , ko.observ#ble(new ro*ile())

    sel*.p6one?u'bers , ko.observ#bleArr#!(@new 6oneCine()F)

    }

    else {

    v#r currentro*ile , G.8rep(u''!ro*ile+ *unction (e) { return

    e.ro*ileId ,, pro*ileId })

    sel*.pro*ile , ko.observ#ble(new ro*ile(currentro*ile@KF))

    v#r currentro*ile6one , G.8rep(6one30+ *unction (e) { return

    e.ro*ileId ,, pro*ileId })

    sel*.p6one?u'bers ,

    ko.observ#bleArr#!(ko.utils.#rr#!p(currentro*ile6one+ *unction (p6one)

    {

    return p6one

    }))

    }

    sel*.#dd6one , *unction () {

  • 7/24/2019 Another MVC Application

    20/43

    sel*.p6one?u'bers.pus6(new 6oneCine())

    }

    sel*.re'ove6one , *unction (p6one)

    { sel*.p6one?u'bers.re'ove(p6one) }

    sel*.b#ck3oro*ileCist , *unction () { window.loc#tion.6re* ,J/cont#ctJ }

    sel*.s#vero*ile , *unction () {

    #lert("#te to s#ve is " H

    ES0?.strin8i*!(ko.toES(sel*.pro*ile())))

    }

    }

    Step ';:

    'e>t we will add one more section to add &hone !nformation in *reateEdit.cshtml page,that

  • 7/24/2019 Another MVC Application

    21/43

    And to edit e>isting *ontact is9

    So now we left only with below re-uirement9

    0.C $ser should able to add any number of Addresses by clic#ing on Add new address.

    0.D $ser should able to remove any address

    Step BH9 5he re-uirement no. 0.C and 0.D are similar to &hone !nformation, below is the final

    code for *reateEdit.js and *reateEdit.cshtml files9

    or *reateEdit.js

    v#r url , window.loc#tion.p#t6n#'e

    v#r pro*ileId , url.substrin8(url.l#stInde0*(J/J) H )

  • 7/24/2019 Another MVC Application

    22/43

    v#r u''!ro*ile , @

    {

    "ro*ileId" +

    "irst?#'e" "An#nd"+

    "C#st?#'e" "#nde!"+

    "5'#il" "#n#nd$#n#ndp#nde!.co'"

    }+ {

    "ro*ileId" D+

    "irst?#'e" "Eo6n"+

    "C#st?#'e" "en#"+

    "5'#il" "jo6n$cen#.co'"

    }

    F

    v#r 6one3!pe#t# , @

    {

    "6one3!peId" +

    "?#'e" "Mork 6one"

    }+ {

    "6one3!peId" D+

    "?#'e" "erson#l 6one"

    }

    F

    v#r 6one30 , @

    {

    "6oneId"+

    "6one3!peId" +

    "ro*ileId"+

    "?u'ber" "-DDD-===="

    }+ {

    "6oneId" D+

    "6one3!peId" D+

    "ro*ileId" +

    "?u'ber" "NNN-OOO-PPPP"

    }

    F

    v#r Address3!pe#t# , @

    {

    "Address3!peId" +

    "?#'e" "S6ippin8 Address"

    }+ {

    "Address3!peId" D+

    "?#'e" "Billin8 Address"

    }

    F

    v#r Address30 , @

    {

    "AddressId" +

    "Address3!peId" +

    "ro*ileId" +

    "AddressCine" "KKKK %ic6'ond Avenue"+

    "AddressCineD" "Apt Q KKK"+ "ountr!" "SA"+

  • 7/24/2019 Another MVC Application

    23/43

    "St#te" "3e#s"+

    "it!" "ouston"+

    "Tipode" "UKKKK"

    }+

    {

    "AddressId" D+

    "Address3!peId" D+ "ro*ileId" +

    "AddressCine" "DKKKK i86w#! P"+

    "AddressCineD" "Suite Q DKKK"+

    "ountr!" "SA"+

    "St#te" "3e#s"+

    "it!" "ouston"+

    "Tipode" "9KKKK"

    }

    F

    v#r ro*ile , *unction (pro*ile) {

    v#r sel* , t6is

    sel*.ro*ileId , ko.observ#ble(pro*ile pro*ile.ro*ileId K)

    sel*.irst?#'e , ko.observ#ble(pro*ile pro*ile.irst?#'e JJ)

    sel*.C#st?#'e , ko.observ#ble(pro*ile pro*ile.C#st?#'e JJ)

    sel*.5'#il , ko.observ#ble(pro*ile pro*ile.5'#il JJ)

    sel*.6one30 , ko.observ#bleArr#!(pro*ile pro*ile.6one30 @F)

    sel*.Address30 , ko.observ#bleArr#!(pro*ile pro*ile.Address30

    @F)

    }

    v#r 6oneCine , *unction (p6one) {

    v#r sel* , t6is

    sel*.6oneId , ko.observ#ble(p6one p6one.6oneId K) sel*.6one3!peId , ko.observ#ble(p6one p6one.6one3!peId K)

    sel*.?u'ber , ko.observ#ble(p6one p6one.?u'ber JJ)

    }

    v#r AddressCine , *unction (#ddress) {

    v#r sel* , t6is

    sel*.AddressId , ko.observ#ble(#ddress #ddress.AddressId K)

    sel*.Address3!peId , ko.observ#ble(#ddress #ddress.Address3!peId

    K)

    sel*.AddressCine , ko.observ#ble(#ddress #ddress.AddressCine JJ)

    sel*.AddressCineD , ko.observ#ble(#ddress #ddress.AddressCineD JJ)

    sel*.ountr! , ko.observ#ble(#ddress #ddress.ountr! JJ)

    sel*.St#te , ko.observ#ble(#ddress #ddress.St#te JJ) sel*.it! , ko.observ#ble(#ddress #ddress.it! JJ)

    sel*.Tipode , ko.observ#ble(#ddress #ddress.Tipode JJ)

    }

    v#r ro*ileollection , *unction () {

    v#r sel* , t6is

    //i* ro*ileId is K+ It 'e#ns re#te new ro*ile

    i* (pro*ileId ,, K) {

    sel*.pro*ile , ko.observ#ble(new ro*ile())

    sel*.p6one?u'bers , ko.observ#bleArr#!(@new 6oneCine()F)

    sel*.#ddresses , ko.observ#bleArr#!(@new AddressCine()F)

    }

  • 7/24/2019 Another MVC Application

    24/43

    else {

    //or ro*ile in*or'#tion

    v#r currentro*ile , G.8rep(u''!ro*ile+ *unction (e) { return

    e.ro*ileId ,, pro*ileId })

    sel*.pro*ile , ko.observ#ble(new ro*ile(currentro*ile@KF))

    //or 6one nu'ber

    v#r currentro*ile6one , G.8rep(6one30+ *unction (e) { returne.ro*ileId ,, pro*ileId })

    sel*.p6one?u'bers ,

    ko.observ#bleArr#!(ko.utils.#rr#!p(currentro*ile6one+ *unction (p6one)

    {

    return p6one

    }))

    //or Address

    v#r currentro*ileAddress , G.8rep(Address30+ *unction (e)

    { return e.ro*ileId ,, pro*ileId })

    sel*.#ddresses ,

    ko.observ#bleArr#!(ko.utils.#rr#!p(currentro*ileAddress+ *unction

    (#ddress) {

    return #ddress }))

    }

    sel*.#dd6one , *unction () { sel*.p6one?u'bers.pus6(new

    6oneCine()) }

    sel*.re'ove6one , *unction (p6one)

    { sel*.p6one?u'bers.re'ove(p6one) }

    sel*.#ddAddress , *unction () { sel*.#ddresses.pus6(new

    AddressCine()) }

    sel*.re'oveAddress , *unction (#ddress){ sel*.#ddresses.re'ove(#ddress) }

    sel*.b#ck3oro*ileCist , *unction () { window.loc#tion.6re* ,

    J/cont#ctJ }

    sel*.s#vero*ile , *unction () {

    sel*.pro*ile().Address30 , sel*.#ddresses

    sel*.pro*ile().6one30 , sel*.p6one?u'bers

    #lert("#te to s#ve is " H

    ES0?.strin8i*!(ko.toES(sel*.pro*ile())))

    }

    }

    ko.#ppl!Bindin8s(new ro*ileollection())

    and, for *reateEdit.cshtml

    1t#ble cl#ss,"t#ble"7

    1tr7

    1t6 colsp#n,"="7ro*ile In*or'#tion1/t67

    1/tr7

    1tr71/tr7

    1tbod! d#t#-bind,Jwit6 pro*ileJ7

    1tr7

    1td7 1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue irst?#'eJ

    pl#ce6older,"irst ?#'e"/7

  • 7/24/2019 Another MVC Application

    25/43

    1/td7

    1td7

    1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue C#st?#'eJ

    pl#ce6older,"C#st ?#'e"/7

    1/td7

    1td7

    1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue 5'#ilJpl#ce6older,"5'#il" /7

    1/td7

    1/tr7

    1/tbod!7

    1/t#ble7

    1t#ble cl#ss,"t#ble"7

    1tr7

    1t6 colsp#n,"="76one In*or'#tion1/t67

    1/tr7

    1tr71/tr7

    1tbod! d#t#-bind,J*ore#c6 p6one?u'bersJ7

    1tr7 1td7

    1select d#t#-bind,"options 6one3!pe#t#+ v#lue

    6one3!peId+ options:#lue J6one3!peIdJ+ options3et J?#'eJ+

    options#ption JSelect 6one 3!pe...J"71/select7

    1/td7

    1td7

    1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue ?u'berJ

    pl#ce6older,"?u'ber" /7

    1/td7

    1td7

    1# cl#ss,"btn btn-s'#ll btn-d#n8er" 6re*,JQJ d#t#-bind,J

    click Gp#rent.re'ove6oneJ7R1/#7

    1/td7 1/tr7

    1/tbod!7

    1/t#ble7

    1p7

    1button cl#ss,"btn btn-s'#ll btn-pri'#r!" d#t#-bind,Jclick #dd6oneJ7Add

    ?ew 6one1/button7

    1/p7

    16r /7

    1t#ble cl#ss,"t#ble"7

    1tr71t6 colsp#n,"O"7Address In*or'#tion1/t671/tr7

    1tbod! d#t#-bind,"*ore#c6 #ddresses"7

    1tr7

    1td colsp#n,"O"7 1select d#t#-bind,"options Address3!pe#t#+ v#lue

    Address3!peId+ options:#lue JAddress3!peIdJ+ options3et J?#'eJ+

    options#ption JSelect Address 3!pe...J"71/select7

    1/td7

    1/tr7

    1tr7

    1td7

    1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue AddressCineJ

    pl#ce6older,"Address Cine" /7

    1p st!le,"p#ddin8-top Op"71input cl#ss,"input-l#r8e"

    d#t#-bind,Jv#lue St#teJ pl#ce6older,"St#te" /71/p7

    1/td7

    1td7

    1input cl#ss,"input-l#r8e" d#t#-bind,J v#lue AddressCineDJ

    pl#ce6older,"Address CineD" /7

  • 7/24/2019 Another MVC Application

    26/43

    1p st!le,"p#ddin8-top Op"71input cl#ss,"input-l#r8e"

    d#t#-bind,Jv#lue ountr!J pl#ce6older,"ountr!" /71/p7

    1/td7

    1td7

    1input cl#ss,"input-l#r8e" d#t#-bind,Jv#lue it!J

    pl#ce6older,"it!" /7

    1p st!le,"p#ddin8-top Op"71input cl#ss,"input-l#r8e"d#t#-bind,Jv#lue TipodeJ pl#ce6older,"Tip ode" /7

    1# cl#ss,"btn btn-s'#ll btn-d#n8er" 6re*,JQJ d#t#-

    bind,Jclick Groot.re'oveAddressJ7R1/#71/p7

    1/td7

    1/tr7

    1/tbod!7

    1/t#ble7

    1p7

    1button cl#ss,"btn btn-s'#ll btn-pri'#r!" d#t#-bind,Jclick #ddAddressJ7Add

    ?ew Address1/button7

    1/p7

    16r /7

    1button cl#ss,"btn btn-s'#ll btn-success" d#t#-bind,Jclicks#vero*ileJ7S#ve ro*ile1/button7

    1input cl#ss,"btn btn-s'#ll btn-pri'#r!" t!pe,"button" v#lue,"B#ck 3o

    ro*ile Cist" d#t#-bind,"clickGroot.b#ck3oro*ileCist" /7

    1script src,"~/Scripts/re#te5dit.js"71/script7

    So finally, Application will display screens as per the re-uirement9

    Screen B9 *ontact 6ist 7 iew all contacts

    Screen 09 *reate 'ew *ontact 7 5his screen should display one blan# screen to providefunctionality as.

  • 7/24/2019 Another MVC Application

    27/43

    Screen F9 $pdate E>isting *ontact 7 5his screen should display screen with selected contact

  • 7/24/2019 Another MVC Application

    28/43

    information details.

    Validation:

    3e are almost done with designing part of our application .5he only thing left now, is to

    manage validation when the user clic#s on KSaveL button. alidation is the major re-uirement

    and now a day@s most ignorant part for any web application. y proper validation, user can

    #now what data needs to be entered. 'e>t in this article, ! am going to discuss +noc#outS

    alidation library which can be downloaded using 'uJet. 6et us chec# some of the most

    common validation scenarios, and how to achieve it using #noc#out validation.

    Scenario ': 7irst +ame is a required field in form

    this.irst'ame P #o.observable().e>tend(Q re-uired9 true R);

    Scenario *: Ma/imum number of character for 7irst +ame should not e/ceed ? andshould not be less than , character

  • 7/24/2019 Another MVC Application

    29/43

    this.irst'ame P #o.observable().e>tend(Q ma>6ength9 D1, min6ength9FR);

    Scenario 9: A#e is a required field in form4 and should be al%ays #reater than '= and

    less than '??

    this.Age P #o.observable().e>tend(Q re-uired9 true, ma>9 B11, min9B2 R);

    Scenario : .mail is a required field and should be in proper email format

    this.Email P #o.observable().e>tend(Q re-uired9 true, email9 true R);

    Scenario ;: 8ate of 5irth is a required field and should be in proper date format

    this.4ate=firth P #o.observable().e>tend(Q re-uired9 true, date9 true R);

    Scenario tend(Q re-uired9 true, number9 true R);

    Scenario =: Phone +umber is a required field and should be in proper -S format

    'ote9 A valid $S phone number has the following format9 B>dd>dddddd

    5he TBT at the beginning of the string is optional and so are the dashes. > is any digit

    between 0 and U while d can be any digit.

    this.&hone P #o.observable().e>tend(Q re-uired9 true, phone$S9 true R);

    Scenario >: !o8ate field must be #reater than 7rom8ate fieldt6is.3o#te , ko.observ#ble().etend({

    e;u#l *unction () { return (v#l 7 G(VQro'#teW).v#l()) v#l v#l H

    "L" }

    })

    Scenario '?: Phone number should accept only )@ 06 ?)> from usersv#r re8e , /X((@K-YF{=})X)(@ .-F)(@K-YF{=})XD(@K-YF{N})/

    t6is.6one?u'ber , ko.observ#ble().etend({ p#ttern re8e })

    So, far we have seen different type of validation scenarios and their synta>; now let us

    implement it in our application. or that first download the library #noc#out.validation.js

    using 'uJet. ight now our validation script is fully completed and should loo# li#e this 9

    v#r ro*ile , *unction (pro*ile) {

    v#r sel* , t6is

    sel*.ro*ileId , ko.observ#ble(pro*ile pro*ile.ro*ileId

    K).etend({ re;uired true })

    sel*.irst?#'e , ko.observ#ble(pro*ile pro*ile.irst?#'e

    JJ).etend({ re;uired true+ '#Cen8t6 OK })

    sel*.C#st?#'e , ko.observ#ble(pro*ile pro*ile.C#st?#'e JJ).etend({

    re;uired true+ '#Cen8t6 OK })

    sel*.5'#il , ko.observ#ble(pro*ile pro*ile.5'#il JJ).etend({ re;uired true+ '#Cen8t6 OK+ e'#il true })

  • 7/24/2019 Another MVC Application

    30/43

    sel*.6one30 , ko.observ#bleArr#!(pro*ile pro*ile.6one30 @F)

    sel*.Address30 , ko.observ#bleArr#!(pro*ile pro*ile.Address30

    @F)

    }

    v#r 6oneCine , *unction (p6one) {

    v#r sel* , t6is sel*.6oneId , ko.observ#ble(p6one p6one.6oneId K)

    sel*.6one3!peId , ko.observ#ble(p6one p6one.6one3!peId

    unde*ined).etend({ re;uired true })

    sel*.?u'ber , ko.observ#ble(p6one p6one.?u'ber

    JJ).etend({ re;uired true+ '#Cen8t6 DO+ p6oneS true })

    }

    v#r AddressCine , *unction (#ddress) {

    v#r sel* , t6is

    sel*.AddressId , ko.observ#ble(#ddress #ddress.AddressId K)

    sel*.Address3!peId , ko.observ#ble(#ddress #ddress.Address3!peId

    unde*ined).etend({ re;uired true })

    sel*.AddressCine , ko.observ#ble(#ddress #ddress.AddressCine JJ).etend({ re;uired true+ '#Cen8t6 KK })

    sel*.AddressCineD , ko.observ#ble(#ddress #ddress.AddressCineD

    JJ).etend({ re;uired true+ '#Cen8t6 KK })

    sel*.ountr! , ko.observ#ble(#ddress #ddress.ountr!

    JJ).etend({ re;uired true+ '#Cen8t6 OK })

    sel*.St#te , ko.observ#ble(#ddress #ddress.St#te

    JJ).etend({ re;uired true+ '#Cen8t6 OK })

    sel*.it! , ko.observ#ble(#ddress #ddress.it!

    JJ).etend({ re;uired true+ '#Cen8t6 OK })

    sel*.Tipode , ko.observ#ble(#ddress #ddress.Tipode

    JJ).etend({ re;uired true+ '#Cen8t6 O })

    }

    After validation our final solution loo#s li#e below screen after clic# on KSaveL button9

  • 7/24/2019 Another MVC Application

    31/43

    So far, we tal#ed about achieving our $! without #nowing any actual implementation

    (databases interaction) i.e. $! is created independently by any designer%developer without#nowing the actual business logic. III5hat@s greatIII

    'e>t, ! will tal# about how to design database and how to implement business logic using

    structured layers.

    Part *: Create 8atabase 8esi#n 0S( Server *??= r*6: 7or

    85A

    rom database design prospective below are the main functionality which need to be

    achieved9

    A contact can have irst 'ame, 6ast 'ame and Email.

    A *ontact can have multiple addresses.

    A contact can have multiple phone numbers.

    5o achieve the contact manager functionality, below database design has been used.

  • 7/24/2019 Another MVC Application

    32/43

    5he relationship between tables are as per below database diagram9

    Part ,: 8esi#n (o#ical (ayers: 7or Core 8evelopers

  • 7/24/2019 Another MVC Application

    33/43

    As discussed earlier, our final structure is9

    'e>t we will discuss the overall structure for our application, in terms of the logical group of

    components into separate layers, which communicates with each other with%without any

    restrictions and each logic has its own goals. 6ayers are an architectural style, and it resolves

    the maintenance and enhancement problems in the long run.

    So let us proceed with adding a class library in our solution and name it as

    Application.*ommon.

    Application3Common :

    5his is a class library application, with some common functionality and can be used by

    different logical layers. or e.g. Security, 6ogging, 5rac#ing, alidation etc. 5he componentsdefined in this layer can not only reuse by other layers in this solution, but can also be utilize

    by other applications. 5o ma#e it easier to change in future, we can use 4ependency !njection

    and abstraction, with minimal change in our application.

    or e>ample, !n this layer we are going to use, a validator component to validate data entry,

    and custom 6ogger to log error or warning.

    elow is the screen shot for Solution folder after adding common class library 9

  • 7/24/2019 Another MVC Application

    34/43

    'e>t, we will add a class library in our solution and name it as Application.*ore.

    Application3Core:

    5he components of this layer implement the system core functionality and encapsulate all the

    relevant business logic. asically, this layer usually contains classes which implement the

    domain logic within their methods. 5his layer also defined a $nit of 3or# contract within the

    *ore 6ayer so it will be able to comply with the &! principle. 5he primary goal is to

    differentiate and clearly separate the behavior of the core domain%business and the

    infrastructure implementation details such as data access and specific repositories lin#ed to aparticular technology such as =, or simply data access libraries or even cross7cutting

    aspects of the architecture. 5hus, by isolating the application *ore functionality, we will

    drastically increase the maintainability of our system and we could even replace the lower

    layers (data access, =, and databases) with low impact to the rest of the application.

    'e>t, we will add a class library in our solution and name it as Application.4A6.

    Application38A(:

  • 7/24/2019 Another MVC Application

    35/43

    5he responsibility of 4A6 is to and provides data access and persistence operations against

    the storage database; maintain multiple sessions and connection with multiple database, etc.

    5he &rimary goal here is to wrap the E *onte>t with an !nterface%*ontract so we can use it

    from the anger and *ore 6ayers with no direct dependencies to E. 5he data persistence

    components provide access to the data hosted within the boundaries of our system (e.g., our

    main database which is within a specific =$'4E4 *='5EV5), and also to the datae>posed outside the boundaries of our system, such as 3eb services of e>ternal systems.

    5herefore, it has components li#e KepositoriesL that provide such functionality to access the

    data hosted within the boundaries of our system, or KService AgentsL that will consume 3eb

    Services e>posed by other e>ternal bac#7end systems. !n addition, this layer will usually have

    base classes%components with reusable code for all the repository classes.

    'e>t, we will add a class library in our solution and name it as Application. epository.

    Application3&epository:

    5his is a *lass 6ibrary and can be accessible only by Application.anager. or each main

    root E'5!5W in our domain, we need to create one repository. asically, epositories are

    classes%components that encapsulate the logic re-uired to access the application data sources.

    5herefore, they centralize common data access functionality so the application can have a

    better maintainability and de7coupling between technology and logic owned by the

    KanagerL and K*oreL layers.

    'e>t, we will add a class library in our solution and name it as Application. 45=.

  • 7/24/2019 Another MVC Application

    36/43

    Application38!O:

    Again this is a class library, which contains different container classes that e>poses properties

    but no methods and communicate between &resentation 6ayer (Application.3eb) and Service

    6ayer (Application.anager). A 4ata 5ransfer =bject is an object that is used to encapsulate

    data, and send it from one subsystem of an application to another. "ere we are going to use

    45=s by the anager layer to transfer data between itself and the $! layer. 5he main benefit

    here is that it reduces the amount of data that needs to be sent across the wire in distributed

    applications. 5hey also ma#e great models in the * pattern. 3e can also use 45=@s to

    encapsulate parameters for method calls. 5his can be useful if a method ta#es more than C or

    D parameters.

    'e>t, we will add a class library in our solution and name it as Application. anager.

    Application3Mana#er :

    5his is a *lass 6ibrary and can be accessible only by Application.3eb. or each module we

    need to declare one anager. 5he primary responsibilities of anager are to accept re-uest

    from $!%3eb layer, then communicate with re-uired repositories and manipulate data based

    on condition then return bac# the response. 5his layer is intermediate between $! and

  • 7/24/2019 Another MVC Application

    37/43

    epositories.

    Application3$eb:

    !n previous article, we have already implemented this layer using avascript dummy data.

    5his is independent AS&.'E5 * web application, and contains only $ser !nterface

    components, li#e html, .asp>, cshtml, * etc. !t can also be any windows application. !t

    communicates with some methods from manager layer, then evaluate results and choosewhether to show an error or pageB or page0 etc. etc. 5his layer use javascript to load a model

    for the presentation, but the data is processed in the server through an aja> re-uest, so the

    server only manage the business logic and the javascript manages the presentation logic.

    5o better understand how layers are communicating with each other, let us recall the initial

    re-uirement9

    Screen ': Contact (ist ) Vie% all contacts

  • 7/24/2019 Another MVC Application

    38/43

    B.B 5his screen should display all the contacts available in 4atabase.

    B.0 $ser should be able to delete any contact.

    B.F $ser should able to edit any contact details.

    B.C $ser should be able to create a new contact.

    5o populate the grid data, on page load, we call JetAll&rofiles() method of

    *ontact*ontroller. 5his method returns all &rofiles e>ist in database as an S=' object, then

    we bind it with avaScript object self.&rofiles. elow is the code to call JetAll&rofiles() from

    contact.js9

    v#r ro*iles:iew&odel , *unction () {

    v#r sel* , t6is

    v#r url , "/cont#ct/ZetAllro*iles"

    v#r re*res6 , *unction () {

    G.8etES0?(url+ {}+ *unction (d#t#) {

    sel*.ro*iles(d#t#)

    }) }

    =n emove button clic# we call 4elete&rofile () method of *ontact*ontroller. 5his method

    returns removes that profile from database. elow is the code to call 4elete&rofile() from

    contact.js9

    sel*.re'overo*ile , *unction (pro*ile) {

    i* (con*ir'("Are !ou sure !ou w#nt to delete t6is pro*ile")) {

    v#r id , pro*ile.ro*ileId

    w#itin8i#lo8({})

    G.#j#({

    t!pe J5C535J+ url Jont#ct/eletero*ile/J H id+ success *unction () { sel*.ro*iles.re'ove(pro*ile) }+

    error *unction (err) {

    v#r error , ES0?.p#rse(err.response3et)

    G("1div71/div7").6t'l(error.&ess#8e).di#lo8({ 'od#l true+

    title "5rror"+ buttons { "0k"

    *unction () { G(t6is).di#lo8("close") } } }).s6ow()

    }+

    co'plete *unction () { closeM#itin8i#lo8() }

    })

    }

    }

    or both *reate 'ew button and Edit lin#, we only redirect to *reateEdit page with id as 1

  • 7/24/2019 Another MVC Application

    39/43

    for *reate new and for edit the profile id of selected row. elow is the code for create&rofile

    and edit&rofile from contact.js9

    sel*.cre#tero*ile , *unction () {

    window.loc#tion.6re* , J/ont#ct/re#te5dit/KJ

    }

    sel*.editro*ile , *unction (pro*ile) {

    window.loc#tion.6re* , J/ont#ct/re#te5dit/J H pro*ile.ro*ileId

    }

    elow is the diagram representation of communication between three major layers9

    Screen *: Create +e% Contact

    5his screen should display one blan# screen to provide functionalities as.

    0.B $ser should be able to Enter his%her irst name, 6ast 'ame and Email Address.

    0.0 $ser should able to add any number of &hone numbers by clic#ing on Add numbers.

    0.F $ser should able to remove any phone number.

    0.C $ser should able to add any number of Addresses by clic#ing on Add new address.

    0.D $ser should able to remove any address.

    0.G *lic# on save button should save *ontact details in 4atabase and user will return bac# in

    *ontact 6ist page.

  • 7/24/2019 Another MVC Application

    40/43

    0.H *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.

    Screen ,: -pdate ./istin# Contact

    5his screen should display screen with selected contact information details.

    F.B $ser should be able to modify his%her irst name, 6ast 'ame and Email Address.

    F.0 $ser should able to modify %delete%Add any number of &hone numbers by clic#ing on

    Add numbers or delete lin#.

    F.F $ser should able to modify %delete%Add any number of Addresses by clic#ing on Add new

    address or delete lin#.

    F.C *lic# on save button should update *ontact details in 4atabase and user will return bac#

    in *ontact 6ist page.F.D *lic# on ac# to &rofile button should return bac# the user to *ontact 6ist page.

  • 7/24/2019 Another MVC Application

    41/43

    As discussed in previous implementation, for both K*reate newL and KEdit e>istingL

    re-uirement we are using single page as *reateEdit.cshtml, by identifying the $6 value for

    profile!d i.e. if profile!d in $6 is 1, then it is re-uest for creating a new profile, and if it is

    some value, the re-uest is for edit e>isting profile. elow id the implementation details9

    !n any case (*reate or Edit), on page load we need to initialize data for &hone5ype and

    Address5ype. or that we have one method in *ontact*ontroller as !nitialize&age4ata().

    elow is the code in *reateEdit.js to initialize both arrays9

    v#r Address3!pe#t#

    v#r 6one3!pe#t#

    G.#j#({

    url urlont#ct H J/Initi#li

  • 7/24/2019 Another MVC Application

    42/43

    }

    })

    'e>t, or Edit profile we need to get the profile data, for that we have Jet&rofiley!d()

    method in our *ontact*ontroller. 3e modify our e>isting *reateEdit.js code as9

    G.#j#({

    url urlont#ct H J/Zetro*ileB!Id/J H pro*ileId+

    #s!nc *#lse+

    d#t#3!pe JjsonJ+

    success *unction (json) {

    sel*.pro*ile , ko.observ#ble(new ro*ile(json))

    sel*.p6one?u'bers ,

    ko.observ#bleArr#!(ko.utils.#rr#!p(json.6one30+ *unction (p6one) {

    return p6one

    }))

    sel*.#ddresses ,

    ko.observ#bleArr#!(ko.utils.#rr#!p(json.Address30+ *unction (#ddress) {

    return #ddress

    }))

    }

    })

    inally, or Save data we have two methods in database. !f it@s *reate new the we will call

    Save&rofile!nformtion() method of *ontact*ontroller else we will call

    $pdate&rofile!nformation() method. 3e modify our e>isting *reateEdit.js code as9

    G.#j#({

    t!pe (sel*.pro*ile().ro*ileId 7 K J3J J0S3J)+

    c#c6e *#lse+

    d#t#3!pe JjsonJ+

    url urlont#ct H (sel*.pro*ile().ro*ileId 7 K J/pd#tero*ileIn*or'#tionid,J H

    sel*.pro*ile().ro*ileId J/S#vero*ileIn*or'#tionJ)+

    d#t# ES0?.strin8i*!(ko.toES(sel*.pro*ile()))+

    content3!pe J#pplic#tion/json c6#rset,ut*-9J+

    #s!nc *#lse+

    success *unction (d#t#) {

    window.loc#tion.6re* , J/cont#ctJ

    }+

    error *unction (err) {

    v#r err , ES0?.p#rse(err.response3et)

    v#r errors , ""

    *or (v#r ke! in err) {

    i* (err.6#s0wnropert!(ke!)) { errors H, ke!.repl#ce("pro*ile."+ "") H " " H err@ke!F

    }

    }

    G("1div71/div7").6t'l(errors).di#lo8({ 'od#l true+

    title ES0?.p#rse(err.response3et).&ess#8e+ buttons { "0k"

    *unction () { G(t6is).di#lo8("close") } } }).s6ow()

    }+

    co'plete *unction () {

    }

    })

  • 7/24/2019 Another MVC Application

    43/43

    Conclusion

    5hat