entity framework in a multi layered application.docx

Upload: zoranmatusko

Post on 01-Jun-2018

222 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    1/19

    ENTITY FRAMEWORK IN A MULTI

    LAYERED APPLICATIONIn this blog I'll show you how you can use Entity Frameworkin an MVC.Net website, with a multi layered architecture.The main goals o the architecture to make unit testing andintegration testing easy, and kee! a clean se!eartion oconcerns. To achie"e this goal I will be using the conce!t o#e!endency In$ection %#I& and uto ac als IoC Container (#I ramework. ). IntroductionLet me start by introducing the layered architecture that I like touse for my web applications:

    Database to store data. Data Access layer which contains the linq queries that are executed

    against ntity !ramework. Domain "er#ices layer$ which holds the business logic and workflow

    logic. %&'.(et website which talks only to the Domain "er#ices layer. )'! ser#ices which talk only to the Domain "er#ices layer.

    *his isn+t an uncommon approach. %ain ad#antages are cleanseperation of layers and easy reuse of domain logic by the %&'.(etwebsite and the )'! ser#ices ,and windows ser#ices if you like-.*. +etting u! the solution *.) #ata ccess ayerLet+s start with creating the data access layer. In this example weha#e two entities:

    public class mployee public #irtual int Id get/ set/ 0 public #irtual string (ame get/ set/ 0 public #irtual double "alary get/ set/ 0 public #irtual Date*ime1 2ob ndDate get/ set/ 0 public #irtual 3rganisation 3rganisation get/ set/ 0 0 public class 3rganisation public #irtual int Id get/ set/ 0 public #irtual string (ame get/ set/ 0 public #irtual List4 mployee5 mployees get/ set/ 0 0

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    2/19

    (ow we can add the entity framework context. I use 'ode !irst$ sothis is the context: public class Database'ontext : Db'ontext public Db"et4 mployee5 mployees get/ set/ 0 public Db"et43rganisation 5 3rganisations get/ set/ 0 0 (ext we add two Data Access classess$ which contain the linqqueries on top of the ntity !ramework context: public class 3rganisationDa public 3rganisation 6et7yId,int id- #ar ctx 8 new Database'ontext ,-/ return ctx.3rganisations."ingle,it 85 it.Id 88 id-/ 0 0 public class mployeeDa public #oid Add, mployee employee-

    #ar ctx 8 new Database'ontext ,-/ ctx. mployees.Add,employee-/ ctx."a#e'hanges,-/ 0 0 *o summari9e$ this is what the DataAccess pro ect looks like now:

    )e ha#e a Database'ontext$ an mployee entity$ an mployeeDataAccess class$ an 3rganisation entity and an 3rganisationDataAccess class.

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    3/19

    *.* #omain +er"ices ayer )e can start to build the Domain "er#ices Layer$ and use theDataAccess classes: public class mployee"er#ice public #oid Add mployee,string name$ int organisationId- #ar organisation 8 new3rganisationDa,-.6et7yId,organisationId-/

    #ar employee 8 new mployee,-/ employee.(ame 8 ;2ohn;/ employee.3rganisation 8 organisation/

    #ar employeeDa 8 new mployeeDa,-/ employeeDa.Add,employee-/ 00 As you can see we create a new employee and connect that to anexisting organisation$ (ext we can use this ser#ice in an %&'.(etpro ect. *.- MVC ayer public class

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    4/19

    -. First !roblem Multi!le Entity Frameworkconte/ts-.) IntroductionIf we would run the solution we created and the Index method of

    the

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    5/19

    public 3rganisationDa,Database'ontext databasecontext- ?databasecontext 8 databasecontext/ 0

    public 3rganisation 6et7yId,int id- return ?databasecontext.3rganisations."ingle,it 85 it.Id 88id-/ 0 0 public class mployeeDa pri#ate Database'ontext ?databasecontext/

    public mployeeDa,Database'ontext databasecontext- ?databasecontext 8 databasecontext/ 0

    public mployee 6et7yId,int id- return ?databasecontext. mployees."ingle,it 85 it.Id 88 id-/ 0

    public #oid Add, mployee employee-

    ?databasecontext. mployees.Add,employee-/ ?databasecontext."a#e'hanges,-/ 0 0 If we run the solution now$ and execute the Index method of the

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    6/19

    0. +econd !roblem Not 1nit Testable0.) Introduction3ur Domain "er#ices can+t be unit tested$ but only integration testscan be written$ because it uses the data access layer and the

    database. Let+s introduce a new method ,6i#e=aise- in the mployee"er#ice$that we went to test: public class mployee"er#ice public #oid 6i#e=aise,int employeeId$ double raise- #ar database'ontext 8 new Database'ontext,-/

    #ar employeeDa 8 new mployeeDa,database'ontext-/ #ar employee 8 employeeDa.6et7yId,employeeId-/

    if ,employee.2ob ndDate.

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    7/19

    database. "o this test can+t be run. And e#en if there+s a database$we don+t want to test the database$ we want to test the logic insidethe Domain "er#ice.0.* +olution one In Memory #atabaseIf we don+t want to use a real database$ we can use an inFmemorydatabase. )e ha#e to change the code of the mployee"er#icea bit: public class mployee"er#ice Database'ontext ?database'ontext/

    public mployee"er#ice,Database'ontext database'ontext- ?database'ontext 8 database'ontext/ 0

    public #oid 6i#e=aise,int employeeId$ double raise- #ar employeeDa 8 new mployeeDa,?database'ontext-/ #ar employee 8 employeeDa.6et7yId,employeeId-/

    if ,employee.2ob ndDate.

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    8/19

    #ar working mployee 8 new mployee,- Id 8 > 0/

    database'ontext. mployees.Add,working mployee-/

    database'ontext."a#e'hanges,-/

    #ar employee"er#ice 8 new mployee"er#ice,context-/

    Act #ar employee 8 employee"er#ice.6i#e=aise,>$ >EE-/

    Assert Assert.Are qual,>EE$ employee."alary-/0.- +olution two Mock ob$ectsIf we don+t want to do anything with the DataAccess layer$ we canuse a fake DataAccess Layer$ that doesn+t need a database but ustreturns an employee.

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    9/19

    public #oid Add, mployee employee- ?database'ontext. mployees.Add,employee-/ ?database'ontext."a#e'hanges,-/ 0 0 (ow we can create a mock of I mployeeDa in the test: #ar employeeDa%ock 8 new %ock4I mployeeDa5,-/ employeeDa%ock ."etup4 mployee5,it 85 it.6et7yId,>-- .=eturns,new mployee,- Id 8 >$ "alary 8 E 0-/ #ar employeeDa 8 employeeDa%ock.3b ect/ "o how do we use this mocked mployeeDa in the

    mployee"er#ice )e can in ect it in the constructor$ assign it to apri#ate field$ and use it in the methods like this: public class mployee"er#ice Database'ontext ?database'ontext/ I mployeeDa ?employeeDa/

    public mployee"er#ice,Database'ontext database'ontext$

    I mployeeDa employeeDa- ?database'ontext 8 database'ontext/ ?employeeDa 8 employeeDa/ 0

    public mployee 6i#e=aise,int employeeId$ double raise-

    #ar employee 8 ?employeeDa.6et7yId,employeeId-/ if ,employee.2ob ndDate.

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    10/19

    return employee/

    0 0 *o conclude this chapter$ this is the complete test:B*est%ethodC public #oid )orking mployee'an6et=aise,- Arrange #ar context 8 new Database'ontext,-/

    #ar employeeDa%ock 8 new %ock4I mployeeDa5,-/ employeeDa%ock ."etup4 mployee5,it 85 it.6et7yId,>-- .=eturns,new mployee,- Id 8 >$ "alary 8 E 0-/

    #ar employeeDa 8 employeeDa%ock.3b ect/

    #ar employee"er#ice 8 new mployee"er#ice,context$employeeDa-/

    Act #ar employee 8 employee"er#ice.6i#e=aise,>$ >EE-/

    Assert Assert.Are qual,>EE$ employee."alary-/

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    11/19

    2. Third !roblem 1nwanted EF 3ueries in#omain +er"ices2.) Introduction"ince the Domain "er#ices ha#e access to the ntity !ramework

    context$ it+s possible to write queries to directly load entities fromthe database. *his goes against the "ingle =esponsibility Grincipleand "eperation of 'oncerns. It+s possible to do this in the mployee"er#ice: public mployee 6i#e=aise,int employeeId$ double raise- #ar employee 8 ?database'ontext. mployees .Include,;3rganisation;-

    ."ingle,it85it.Id 8 employeeId-/

    ... 0 )hat+s wrong about this1 *he main problem is that once we startthis way$ soon a lot of methods in the Domain "er#ices Layer willha#e queries on top of ntity !ramework.

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    12/19

    0 0 Hse the Hnit3f)ork in the Domain "er#ices Layer this way: public class mployee"er#ice : I mployee"er#ice Hnit3f)ork ?unit3f)ork/ I mployeeDa ?employeeDa/ I3rganisationDa ?organisationDa/

    public mployee"er#ice,Hnit3f)ork unit3f)ork$ I mployeeDa employeeDa$ I3rganisationDa organisationDa- ?unit3f)ork 8 unit3f)ork/ ?employeeDa 8 employeeDa/ ?organisationDa 8 organisationDa/ 0

    public mployee 6i#e=aise,int employeeId$ double raise- #ar employee 8 ?employeeDa.6et7yId,employeeId-/

    if ,employee.2ob ndDate.

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    13/19

    )hen we go back to the the %&'.(et pro ect )eb Layer$ we ha#eto write this code to gi#e an employee a raise: public class

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    14/19

    organisationDa-/ 0

    public Action=esult Index,- ?employee"er#ice.6i#e=aise,>$ >EE-/

    return &iew,-/ 0 0 *here are two things nice about the code abo#e: >. *he index,- method doesn+t ha#e to worry about initialising the

    mployee"er#ice anymore/ . If we want to test the Index,- method$ we can in ect theDatabase'ontext$ so we can use for example an in memorydatabase. 7ut there are also a few things that aren+t so great: >. )e ha#e access to the Database'ontext and can write directqueries on top of ntity !ramework/ . )e can+t mock the mployee"er#ice/ J. If the mployee"er#ice needs another DataAccess class$ weha#e to add it in the constructor of the controller too. *he morecontrollers there are$ the more work this is. *his quickly gets pretty

    tedious. *herefore$ let+s look at part two of the solution6.- +olution two 1se an IoC containerIo' containers are used to gi#e control o#er instantiating ob ect toan external framework. Kou set them up once$ and the create andin ect the ob ect e#erytime it is needed. In the

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    15/19

    #ar builder 8 new Autofac.'ontainer7uilder,-/

    builder.=egister'ontrollers,typeof,%#cApplication-.Assembly-.GropertiesAutowired,-/

    BInsert custom initialisation hereC

    #ar container 8 builder.7uild,-/

    Dependency=esol#er."et=esol#er,newAutofacDependency=esol#er,container--/ 0 As Autofac is setup$ we can start to register our "er#ices andDataAccess classes. I like to do that in a new pro ect$ to keep the)eb layer clean of all references that are not neccessary. I namethis pro ect AutofacInitialiser. !irst we register the DataAcces classes namespace Mample.AutofacInitialiser public class DataAccess%odule : Autofac.%odule

    protected o#erride #oid Load,'ontainer7uilder builder- builder.=egisterAssembly*ypes,Assembly.Load,;Mmpl.DataAccess;-- .)here,t 85 t.(ame. nds)ith,;Da;-- .AsImplementedInterfaces,- .InstanceGerLifetime"cope,-/ 0 0

    0 And register this module in the global.asax: protected #oid Application?"tart,- ...

    #ar builder 8 new Autofac.'ontainer7uilder,-/

    builder.=egister'ontrollers,typeof,%#cApplication-.Assembly-.Groper

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    16/19

    tiesAutowired,-/

    builder.=egister%odule,new DataAccess%odule,--/

    #ar container 8 builder.7uild,-/

    Dependency=esol#er."et=esol#er,newAutofacDependency=esol#er,container--/ 0 )hat did we ust do1 )e+#e created a %odule that scans theDataAccess Layer and registers e#ery class the ends with Da inAutofac. *his means that if we write 3rderDa$ or GroductDa$ orwhate#erDa$ they are registered automatically with autofac$ withoutwriting any special code. )e can write a %odule for the Domain "er#ices too: public class Domain"er#ices%odule : Autofac.%odule

    protected o#erride #oid Load,'ontainer7uilder builder-

    builder.=egisterAssembly*ypes,Assembly.Load,;Mmpl.Domain"er#ices;--

    .)here,t 85 t.(ame. nds)ith,;"er#ice;--

    .AsImplementedInterfaces,-

    .InstanceGerLifetime"cope,-/ 0

    0 and register it in the global.asax: protected #oid Application?"tart,-

    ...

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    17/19

    #ar builder 8 new Autofac.'ontainer7uilder,-/

    builder.=egister'ontrollers,typeof,%#cApplication-.Assembly-.GropertiesAutowired,-/

    builder.=egister%odule,new DataAccess%odule,--/ builder.=egister%odule,new Domain"er#ices%odule,--/

    #ar container 8 builder.7uild,-/

    Dependency=esol#er."et=esol#er,newAutofacDependency=esol#er,container--/ 0 And finally we write a module for all ntity !ramework relatedclasses: public class ntity!ramework%odule : Autofac.%odule

    protected o#erride #oid Load,'ontainer7uilder builder-

    builder.=egister%odule,new DataAccess%odule,--/

    builder.=egister*ype4Database'ontext5,-

    .As"elf,-

    .InstanceGerLifetime"cope,-/

    builder.=egister*ype4Hnit3f)ork5,- .As"elf,-

    .InstanceGerLifetime"cope,-/

    0

    0

    and register it in the global.asax:

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    18/19

    protected #oid Application?"tart,- ...

    #ar builder 8 new Autofac.'ontainer7uilder,-/

    builder.=egister'ontrollers,typeof,%#cApplication-.Assembly-.GropertiesAutowired,-/

    builder.=egister%odule,new DataAccess%odule,--/ builder.=egister%odule,new Domain"er#ice%odule,--/ builder.=egister%odule,new ntity!ramework%odule,--/

    #ar container 8 builder.7uild,-/

    Dependency=esol#er."et=esol#er,newAutofacDependency=esol#er,container--/ 0 (ow we can in ect the mployee"er#ice into the

  • 8/9/2019 ENTITY FRAMEWORK IN A MULTI LAYERED APPLICATION.docx

    19/19

    >. )e ha#e no access to the Database'ontext from the )eb Layer$and can+t write direct queries on top of ntity !ramework/ . )e can mock the mployee"er#ice/ J. If the mployee"er#ice needs another DataAccess class$ wedon+t ha#e to add it in the constructor of the controller anymore$and don+t ha#e to add anything to the Autofac inisialisation code. 7 Final thoughtsIn this blog I wrote how to create a &isual "tudio solution with threelayers ,Data Access$ Domain "er#ices and )eb- which are clearlyseperated from each other$ and are easy to test. I+#e achie#ed thisby wrapping the ntity !ramework context and by using Autofac asIo' container.