Vous êtes sur la page 1sur 9

12/20/2016 CorrectuseofRepositoryandUnitOfWorkpatternsinASP.

NETMVC|CodeFizzle

CodeFizzle

CorrectuseofRepositoryandUnitOfWorkpatterns
inASP.NETMVC

JUL26
PostedbyBenoit
HelloWorld!

MyveryrstarticleisrevolvingarroundASP.NETMVCandthecorrectuseofUnitOfWork/
Repositorypaerns.

Thisarticleisnotabouthowtoplumbadependencyinjection(DI)containerinASP.NETMVC,nor
aboutteachingthehows&toofDI.
IamassumingyouarealreadyfamiliarwithInversionofControl(IoC)/DIandthatyouknowhowto
properlyuseyourfavoriteDIcontainerinvariousframeworks(ASP.NETMVCbeingone).

ThisarticleisalsonotdiscussinghowtouseEntityFrameworkcoderst,norASP.NETMVC(youll
ndplentyofbooksandarticlesonthesubject).

ThisarticleisallaboutdesigninganicedomainlayerbasedonUnitofWorkandRepositorypaerns,
whilekeepingitassimpleaspossible(embracingKISS)yetpowerfullandfullyloosecoupled.

TheusageofthislayerwillbeillustratedthroughEntityFrameworkCodeFirstastheproviderand
ASP.NETMVCastheclient,eventhoughbothofthesecouldbereplacedbyothercomponents(thats
oneofthepointofloosecouplingafterall!)

MostoftheASP.NETMVCarticles/booksIhavereadonASP.NETMVCarepresentingbasicdesignsto
accessthedatalayerthroughrepositoriesthatarelookingniceatrstsight,butaretrulynotviable
designs.

Letsstartthisarticlebylookingatsuchapoordesignandwhyitiswrong.

Awrong(alascommon)design

Themostcommonantipaern(Iusedittoo)istouseaninterfacePERrepositorytype(suchas
https://codefizzle.wordpress.com/2012/07/26/correctuseofrepositoryandunitofworkpatternsinaspnetmvc/ 1/9
12/20/2016 CorrectuseofRepositoryandUnitOfWorkpatternsinASP.NETMVC|CodeFizzle

Themostcommonantipaern(Iusedittoo)istouseaninterfacePERrepositorytype(suchas
IOrderRepository,ICustomerRepository,IEmployeeRepository..andsoon),andtoinject(via
constructorinjection)concreterepositoryimplementationsintothecontroller.

ThisisatotallyfunctionalloosecoupleddesignrelyingonDI,allowingdierent
repositoryimplementationsperprovider,easyunittestingandgloballyworkingnehoweverifyou
scratchthesurfaceofthisdesignalilebit,youllstarttoseequiteafewglitches,herearethemain
ones:

Eachrepositoryrequires:oneinterface+oneconcreteclassperprovider(forexampleentity
frameworkorinmemory)

Ifyouarestartingtohavequitesomerepositories,itmeansthatyouwillhavetotype(orcopypaste
arround)alotofcodeforcreatingthenewassociatedtypesandprobablymanylescreatedintheway.
Ifyouhavecustomspecializedmethodsforeachrepository,italsomeansimplementingthemforall
theproviders.

Thejoyofneverendingconstructorparameters

Oneabstracttypeperrepositoryequalsoneparameterinyourcontrollerconstructorperrepositorythat
needstobeaccessedbythecontroller.Ifacontrollerisperformingoperationsonmultiplerepositories,
thiscanquicklybecomeamesswithalotofparametersintheconstructor(evenifconstructorwillnever
becalledexplicitlybyyourcodebutbytheDIcontainerconstructorinjectionitstilllookskindof
crapy).
Moreoverifyouneedtoaddaccesstoanewrepositoryinacontroller,itmeansaddinganewparameter
totheconstructoranddoinganewbindinginyourDIcontainer.

UnitofWorkusageisnonexistent(orrathercompletelywrong)

Byinjectingrepositoriesindividuallyineachcontroller,thetruepoweroftheunitofworkpaernis
completelybypassed.
Indeed,throughthisbasicdesign,toillustrateviaEFCodeFirst,aDbContext(EFUnitOfWork)is
usuallyinstantiatedperrepositoryandatbestaCommitmethodispresentineachrepositorytocall
SaveChangesontheDbContext(andapplyallmodicationstoDB)oncealloperationshavebeendone
ontherepository.AtworsethecalltoSaveChangesontheDbContextisdoneineachrepositorymethod
performingmodicationsintherepository.

Youareusinganiceloosecoupleddesign,playingnicelywithdependencyinjectionandunittestingbut
youareshootingyourselfabulletintheheadbynotcorrectlyusingtheUnitOfWorkpaern.

Themainproblemwiththisincorrectuseoftheunitofworkpaernisthatforaspecicuserrequest,
triggeringcalltoactionmethodandpotentiallyaccessingmultiplerepositories,doingworkonthem,
youarecreatingmultipleunitofworkswhereasasingleunitofworkshouldbeused!Thedenitionof
theUnitOfWorkpaernisratherclear:Maintainsalistofobjectsaectedbyabusinesstransaction
andcoordinatesthewritingoutofchangesandtheresolutionofconcurrencyproblems.
(hp://martinfowler.com/eaaCatalog/unitOfWork.html
(hp://martinfowler.com/eaaCatalog/unitOfWork.html)).Thebusinesstransactionhereistypically
triggeredbytheenduser,indirectlycallinganactionmethod.Itstartswhentheendusertriggersthe
operation,andendswhentheoperationiscompleted,whateverthenumberofrepositoriesaccessedand
thenumberofCRUDoperationsperformedonthem.Thismeansthatasingleunitofworkshouldbe

usedinthecontextoftheoperation/transaction(therequest)andnotmanydierentones.Typically,to 2/9
https://codefizzle.wordpress.com/2012/07/26/correctuseofrepositoryandunitofworkpatternsinaspnetmvc/
12/20/2016 CorrectuseofRepositoryandUnitOfWorkpatternsinASP.NETMVC|CodeFizzle

usedinthecontextoftheoperation/transaction(therequest)andnotmanydierentones.Typically,to
useEntityFrameworkasaproviderexample,followingthisbaddesignwouldresultincalling
SaveChangesmultipletimes,meaningmultipleroundtripstoDBthroughmultipletransactionswhichis
typicallynotthebehaviorwanted(andabsolutelynottheUnitOfWorkphilosophy).

Apartfromtheperformanceaspect,italsoleadstoaproblemwhenanerror/exceptionhappensinthe
middleofanoperationinanactionmethod.Ifyoualreadymadesomechangesinsomerepositoriesand
commitedthechanges,buttheglobaloperationisnotcomplete(potentiallyotherrepositoriesshould
havebeenupdatedaswellbuthavenotbeen),itwillleaveyourpersisteddatapartoftheoperationin
anincoherentstate(Iwishyougoodlucktorollbackeachchanges).Whereasifyouonlyuseasingle
UnitOfWorkfortheoperation,ifitfailsbeforecompleting(beforereachingtheendoftheaction
method),thennodataisupdatedatallpartoftheoperation,yourdatastorestaysclean(anditalsodoes
asingleroundtriptotheDB,inasingletransactionforchangesdoneaccrossallrepositories).

Amuchbetterdesign

Iwentthroughquitesomedierentapproachesandimprovementstomakemydomainlayerbasedon
unitofworkandrepositoryasclean/simpleaspossiblewithaminimumnumberoftypes/les,yetfully
loosecoupledandextensible.

IwillspareyouwithallthevariousintermediatesstepsandIamdirectlygoingtodiscussthenal
(current)design:

FirstofallIdeneanabstractgenericrepositorytype,containingverybasicatomicoperations:

1 publicinterfaceIGenericRepository<T>:whereT:class
2 {
3 IQueryable<T>AsQueryable();
4
5 IEnumerable<T>GetAll();
6 IEnumerable<T>Find(Expression<Func<T,bool>>predicate);
7 TSingle(Expression<Func<T,bool>>predicate);
8 TSingleOrDefault(Expression<Func<T,bool>>predicate);
9 TFirst(Expression<Func<T,bool>>predicate);
10 TGetById(intid);
11
12 voidAdd(Tentity);
13 voidDelete(Tentity);
14 voidAttach(Tentity);
15 }

Ithendeneanabstractunitofworktype,containingallthegenericrepositoriesbeingpartoftheunit
ofwork,alongwithasingleCommit()methodusedtopersistallchangesdoneintherepositoriestothe
underlyingdatastore(pleasenotethatOrder,CustomerandEmployeearepurePOCOclasses).

1 publicinterfaceIUnitOfWork:IDisposable
https://codefizzle.wordpress.com/2012/07/26/correctuseofrepositoryandunitofworkpatternsinaspnetmvc/ 3/9
12/20/2016 CorrectuseofRepositoryandUnitOfWorkpatternsinASP.NETMVC|CodeFizzle

1 publicinterfaceIUnitOfWork:IDisposable
2 {
3 IGenericRepository<Order>OrderRepository{get;}
4 IGenericRepository<Customer>CustomerRepository{get;}
5 IGenericRepository<Employee>EmployeeRepository{get;}
6
7 voidCommit();
8 }

NowforeachdataproviderweneedtomakeasingleconcreteimplementationofIUnitOfWorkand
IGenericRepositorywhateverthenumberofrepositoriesandwearegoodtogo!

Iwillillustrateimplementatingtheseinterfaces,throughimplementationstargetingEntityFramework
(coderst).

FirstofallEfUnitOfWork.
ThisclassisimplementingIUnitOfWorkandalsoinheritsfromDbContext(EFCodeFirstunitofwork).
ItcontainsDbSets,whichcanbeseenasrepositoriesfromEFpointofview(infactinEFCodeFirst,you
cansubstituteinyourmindthewordUnitOfWorkwithDbContextandRepositorywith
DbSet).
TheconstructorjustinstanciatealltherepositoriesbypassingthemthecorrespondingDbSetintheir
constructor.Thiscanbeimprovedbyinstantiatingrepositoriesonlywhentheyareaccessed.Indeedif
yourunitofworkcontains20repositoriesandyourcontrollerisjustgoingtouseone,thisisalotof
uselessinstantiations.

1 publicclassEfUnitOfWork:DbContext,IUnitOfWork
https://codefizzle.wordpress.com/2012/07/26/correctuseofrepositoryandunitofworkpatternsinaspnetmvc/ 4/9
12/20/2016 CorrectuseofRepositoryandUnitOfWorkpatternsinASP.NETMVC|CodeFizzle

1 publicclassEfUnitOfWork:DbContext,IUnitOfWork
2 {
3 privatereadonlyEfGenericRepository<Order>_OrderRepo;
4 privatereadonlyEfGenericRepository<Customer>_customerRepo;
5 privatereadonlyEfGenericRepository<Employee>_employeeRepo;
6
7 publicDbSet<Order>Orders{get;set;}
8 publicDbSet<Customer>Customers{get;set;}
9 publicDbSet<Employee>Employees{get;set;}
10
11 publicEfUnitOfWork()
12 {
13 _orderRepo=newEfGenericRepository<Order>(Orders);
14 _customerRepo=newEfGenericRepository<Customer>(Customers);
15 _employeeRepo=newEfGenericRepository<Employee>(Employees);
16 }
17
18 #regionIUnitOfWorkImplementation
19
20 publicIGenericRepository<Order>OrderRepository
21 {
22 get{return_orderRepo;}
23 }
24
25 publicIGenericRepository<Customer>CustomerRepository
26 {
27 get{return_customerRepo;}
28 }
29
30 publicIGenericRepository<Employee>EmployeeRepository
31 {
32 get{return_employeeRepo;}
33 }
34
35 publicvoidCommit()
36 {
37 this.SaveChanges();
38 }
39
40 #endregion
41 }

Thentheclassimplementingtheabstractgenericrepository,whichjustdelegatesallcallstothe
associatedEntityFrameworkDbSet:

1 publicclassEfGenericRepository<T>:IGenericRepository<T>
https://codefizzle.wordpress.com/2012/07/26/correctuseofrepositoryandunitofworkpatternsinaspnetmvc/ 5/9
12/20/2016 CorrectuseofRepositoryandUnitOfWorkpatternsinASP.NETMVC|CodeFizzle

1 publicclassEfGenericRepository<T>:IGenericRepository<T>
2 whereT:class
3 {
4 privatereadonlyDbSet<T>_dbSet;
5
6 publicEfGenericRepository(DbSet<T>dbSet)
7 {
8 _dbSet=dbSet;
9 }
10
11 #regionIGenericRepository<T>implementation
12
13 publicvirtualIQueryable<T>AsQueryable()
14 {
15 return_dbSet.AsQueryable();
16 }
17
18 publicIEnumerable<T>GetAll()
19 {
20 return_dbSet;
21 }
22
23 publicIEnumerable<T>Find(Expression<Func<T,bool>>predicate)
24 {
25 return_dbSet.Where(predicate);
26 }
27
28 //Andsoon...
29
30 #endregion
31 }

Thatsit!Nowifyouneedtoimplementanotherprovider,letssayInMemory(usefullforunit
testing)allyouhavetodoistocreatetwootherclassesInMemGenericRepositoryand
InMemUnitOfWorkusingaHashSetforexampleasthedatastore.

Thatlooksmuchbeerthattheoriginalantipaerndesignnow!

Anditsusage

LetssayyourHomeControllerneedstoaccessallthreedierentrepositories.
Withtheolddesign,yourHomeControllerconstructorsignaturewouldhavelookedas

1 publicHomeController(IOrderRepositoryorderRepo,ICustomerRepositorycustomerRepo,

AndofcoursenewrepositorytobeusedbyHomeController,meansnewparameter+newprivateeld
+newbindinginDIcontainer.

Withtheimproveddesign,nowthesignaturelooksmuchcleaner

1 publicHomeController(IUnitOfWorkunitOfWork)
Andthegoodpointisthatifweaddnewrepositoriesthatwewanttouseinthecontrollerwedonthave
https://codefizzle.wordpress.com/2012/07/26/correctuseofrepositoryandunitofworkpatternsinaspnetmvc/ 6/9
12/20/2016 CorrectuseofRepositoryandUnitOfWorkpatternsinASP.NETMVC|CodeFizzle

Andthegoodpointisthatifweaddnewrepositoriesthatwewanttouseinthecontrollerwedonthave
tochangeanythinginthecontrollerclass,norbindingsinDIcontainer.Wecanjustusethenew
repositorydirectlyfromthecontrollerthroughtheunitofwork.
Nomorethinkingaboutwhichrepositoriesshouldthecontrollerhaveaccesstooandcustomizingthe
constructorassuch.Nowallcontrollersconstructorsneedingtoaccessrepositoriesjustneedasingle
unitOfWorkparameter.

OnDIcontainerside,allthatisneededistobindtheabstractIUnitOfWorktothedesiredprovider
implementation(EF,InMem,other).Youllalsowanttomakesurethatthedependencyiscreatedina
perrequestscope(consideringyourDIcontainerallowthis),meaningthatasingleunitofworkwill
beinstanciatedperrequestandnoteachtimethedecencyisrequired.

Neat!Butsomethingiskindofmissing.

Indeed,throughIGenericRepositoryyoujusthaveaccesstoverybasicatomicoperationsonrepositories
whichisnotverycoolandwillsurelygoagainsttheDRYprinciplesoonerorlater.
Letssayyouneedinmultipleplacesinyourclientcodetorunacomplexqueryonaspecicrepository
(IOrderRepositorysobeit).
Throughtheolddesignitsrelativelystraightforward,youwouldjustdeneamethodin
IOrderRepositoryandimplementitforalltheconcreteproviders(whatapain!).
HoweverwiththeimproveddesignwecantaddthismethodtoIGenericRepository<T>.

Hopefully,extensionmethodsarecomingtotherescue!

TheideaistoaddspecializedmethodstoconstructedgenericIGenericRepository<>typesthrough
extensionmethods.
Forexampleforanhypotheticalmethod,takingtwoparameters,runningaqueryonOrderrepository
andreturninganenumerableofordersyouwoulddosomethinglike:

1 publicstaticclassGwOrderRepositoryExtension
2 {
3 publicstaticIEnumerable<Order>GetOrdersMatchingParams(thisIGenericRepositor
4 {
5 //Doqueryhereandreturntheresult
6 }
7 }

Theninyourcodeyoucancallthismethodontheorderrepositoryeasily:

1 IEnumerable<Order>result=unitOfWork.OrderRepository.GetOrdersMatchingParams(1,"F

Theadvantagecomparedtotheolddesignisthatallofthesespecializedmethodareactingongeneric
repositoriesandthereforedonotneedadierentimplementationperspecializedprovider!Asingle
implementationtorulethemall.

Youmayalsoneedtohavesomereusablemethodsthatareperformingoperationsonmultiple
repositories.Inthiscontext,youshouldjusthavetogothroughastaticclasscontainingextensions
methodsforIUnitOfWork,givingyouaccesstoallrepositories.HowevermakesurenottocallCommit

intheseextensionsmethods(unlessabsolutelynecessary).CommitshouldbetriggeredbytheUnitof
https://codefizzle.wordpress.com/2012/07/26/correctuseofrepositoryandunitofworkpatternsinaspnetmvc/ 7/9
12/20/2016 CorrectuseofRepositoryandUnitOfWorkpatternsinASP.NETMVC|CodeFizzle

intheseextensionsmethods(unlessabsolutelynecessary).CommitshouldbetriggeredbytheUnitof
Workclient(youractionmethodinASP.NETMVCcontext,attheendofthetransaction(endofaction
method)).

Onceagainthisdesignisprobablynotperfect,butstillismuchbeerthanthetypicalbasicapproachof
theproblemandmakesacorrectuseoftheUnitOfWorkpaern.

Oneoftheproblemwiththisdesignisthatyoukindofloosetheinjectiongranularityatarepository
level.YoucantforexamplegothrougharepositorytargetingEFandatthesametimeanother
repositorytargetingInMem.ButthisisrarelyneededIthinkandsomesolutionscouldbesurelythought
of.

Thisconcludesmyrstarticle,hopeyouenjoyedandthatitwasofsomehelptoyou!

Ifyouhaveanyquestionsorremarks,feelfreetoleaveacomment.
Abouttheseads(https://wordpress.com/abouttheseads/)

AboutBenoit

IamaFrenchSoftwareEngineer,recentlyrelocatedtothewonderfulSanFranciscoBayArea.Technogeek,
passionateaboutcomputerssincemychildhood,Ijustlovetocodeandcreate!
ViewallpostsbyBenoit
PostedonJuly26,2012,inDesignandtaggedASP.NETMVC,C#,EntityFrameworkCodeFirst,Loose
Coupling,RepositoryPaern,UnitOfWorkPaern.Bookmarkthepermalink.3Comments.

kumar|May12,2013at2:21pm
Cool,Veryclean

voidqc|May31,2013at10:59am
Nevereverdeletethisblog!Ireallylikethisimplementationofthesepaerns.Istartedfromyour
example,whichIaddedsomeimprovements,touseinabigwebprojectwithmyteam,anditworks
great!

VegardA.Larsen(@vegardlarsen)|June7,2013at2:27am

Weweredoingexactlyasyoudescribedasthewrongwayinaprojectwerecentlystarted.Oneday 8/9
https://codefizzle.wordpress.com/2012/07/26/correctuseofrepositoryandunitofworkpatternsinaspnetmvc/
12/20/2016 CorrectuseofRepositoryandUnitOfWorkpatternsinASP.NETMVC|CodeFizzle

Weweredoingexactlyasyoudescribedasthewrongwayinaprojectwerecentlystarted.Oneday
refactoringandeverythingiseasier.Thankyou.

BlogatWordPress.com.

https://codefizzle.wordpress.com/2012/07/26/correctuseofrepositoryandunitofworkpatternsinaspnetmvc/ 9/9

Vous aimerez peut-être aussi