Vous êtes sur la page 1sur 30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

chsakell's Blog
ANYTHING AROUND ASP.NET MVC,WEB API, WCF, ENTITY
FRAMEWORK & ANGULARJS

HOME ASP.NET MVC ASP.NET MVC SOLUTION ARCHITECTURE BESTPRACTICES

ASP.NETMVCSolutionArchitecture
BestPractices
BY CHRISTOS S. on FEBRUARY 15, 2015

( 96 )

ChoosingtherightarchitectureforWebApplicationsisamust,especiallyforlargescaleones.
UsingthedefaultVisualStudio ASP.NETMVCWebApplication projecttemplates,adding
controllerswith Scaffolding options,justtobootstrapyourapplicationandcreatepagesanddata
injustafewminutes,soundsawesomeforsure,butletsbehonestitsnotalwaystheright
choise.Peekingallthedefaultoptions,keepingbusiness,dataandpresentationlogicinthesame
projectwillimpactseveralfactorsinyoursolutions,suchasscalability,usabilityortestability.In
thispost,willseehowtokeepthingsclean,creatingahighlylooselycoupledASP.NETMVC
Solution,whereDataAccess,BusinessandPresentationlayersaredefinedintherightmanner.
Todothis,wellmakeuseofseveralpatternsandframeworks,someofthosearepresented
below.
1.EntityFrameworkCodeFirstdevelopment
2.GenericRepositoryPattern
3.DependencyInjectionusingAutofacframework
4.Automapper

Thearchitecturewewilltrytobuildinthispostissummarizedinthefollowingdiagram.
Letsstart.AssumingwewanttobuiltaneshopWebApplicationcalledStore,createablank
solutionwiththesamename.

https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

1/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

Models
Addaclasslibraryprojecttothesolution,namedStore.Model.Thislibraryiswherewellkeepall
ofourdomainobjects. EntityFramework willcountontheminordertobuildthedatabasebutwe
arenotgoingtoconfigureCodeFirstusingDataAnnotationsattributesonthisproject.Instead,we
aregoingtoputallthe CodeFirst configurationinspecificConfigurationclassesusingthe
FluentAPI .AddafoldernamedModelsandaddthefollowingtwosimpleclasses.
Gadget.cs
1
2
3
4
5
6
7
8
9
10
11

publicclassGadget
{
publicintGadgetID{get;set;}
publicstringName{get;set;}
publicstringDescription{get;set;}
publicdecimalPrice{get;set;}
publicstringImage{get;set;}

publicintCategoryID{get;set;}
publicCategoryCategory{get;set;}
}

Category.cs
1
publicclassCategory
2
{
3
publicintCategoryID{get;set;}
4
publicstringName{get;set;}
5
publicDateTime?DateCreated{get;set;}
6
publicDateTime?DateUpdated{get;set;}
7

8
publicvirtualList<Gadget>Gadgets{get;set;}
9

10
publicCategory()
11
{
12
DateCreated=DateTime.Now;
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

2/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

12
13
14

DateCreated=DateTime.Now;
}
}

IprefferedtokeepthenamespaceStore.ModelinsteadofthenamespaceStore.Model.Models

DataAccessLayerandRepositories
Thepurposeof
thislayer,isthe
directaccessto
thedatabase.
Itstheonly
layer
responsibleto
communicate
withthe
database.If
someother
layerwantsto
accessthe
database,then
thiswillbe
donethrough
someofthe
classes
(repositories)
wewilldefineinthisproject.ThiswillbestrictlytheonlywayAddanewclasslibraryproject
namedStore.Dataandmakesureyouaddareferencetothepreviouscreatedproject,
Store.Model.InstallEntityFrameworkusingthe NugetPackageManager .Firstthingwewanna
do,istodefinethe EntityTypeConfigurations forourdomainobjecs.Addafoldernamed
Configurationwiththefollowingtwoclassesthatinheritsthe EntityTypeConfiguration class.
GadgetConfiguration.cs
1
2
3
4
5
6
7
8
9
10

publicclassGadgetConfiguration:EntityTypeConfiguration<Gadget>
{
publicGadgetConfiguration()
{
ToTable("Gadgets");
Property(g=>g.Name).IsRequired().HasMaxLength(50);
Property(g=>g.Price).IsRequired().HasPrecision(8,2);
Property(g=>g.CategoryID).IsRequired();
}
}

CategoryConfiguration.cs
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

3/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

CategoryConfiguration.cs
1
2
3
4
5
6
7
8

publicclassCategoryConfiguration:EntityTypeConfiguration<Category>
{
publicCategoryConfiguration()
{
ToTable("Categories");
Property(c=>c.Name).IsRequired().HasMaxLength(50);
}
}

Thereisntanydifficultconfigurationtoexplain,thepointisjusttounderstandwheretoputthe
rightobjects.Thenextthingwewilldoistocreatethe DbContext classthatwillberesponsibleto
accessthedatabase.Addthefollowingclassundertherootofthecurrentproject.
StoreEntities.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

publicclassStoreEntities:DbContext
{
publicStoreEntities():base("StoreEntities"){}

publicDbSet<Gadget>Gadgets{get;set;}
publicDbSet<Category>Categories{get;set;}

publicvirtualvoidCommit()
{
base.SaveChanges();
}

protectedoverridevoidOnModelCreating(DbModelBuildermodelBuilder)
{
modelBuilder.Configurations.Add(newGadgetConfiguration());
modelBuilder.Configurations.Add(newCategoryConfiguration());
}
}

Wewanttoseedthedatabasewhenourapplicationfireupforthefirsttime,soaddthefollowing
classtotherootoftheprojectaswell.
StoreSeedData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

publicclassStoreSeedData:DropCreateDatabaseIfModelChanges<StoreEntities>
{
protectedoverridevoidSeed(StoreEntitiescontext)
{
GetCategories().ForEach(c=>context.Categories.Add(c));
GetGadgets().ForEach(g=>context.Gadgets.Add(g));

context.Commit();
}

privatestaticList<Category>GetCategories()
{
returnnewList<Category>
{
newCategory{

16
Name="Tablets"
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

4/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

Name="Tablets"
},
newCategory{
Name="Laptops"
},
newCategory{
Name="Mobiles"
}
};
}

privatestaticList<Gadget>GetGadgets()
{
returnnewList<Gadget>
{
newGadget{
Name="ProntoTec7",
Description="Android4.4KitKatTabletPC,CortexA81.2
CategoryID=1,
Price=46.99m,
Image="prontotec.jpg"
},
newGadget{
Name="SamsungGalaxy",
Description="Android4.4KitKatOS,1.2GHzquadcorep
CategoryID=1,
Price=120.95m,
Image="samsunggalaxy.jpg"
},
newGadget{
Name="NeuTabN7Pro7",
Description="NeuTabN7Protabletfeaturestheamazingp
CategoryID=1,
Price=59.99m,
Image="neutab.jpg"
},
newGadget{
Name="DragonTouchY88X7",
Description="DragonTouchY88Xtabletfeaturingtheincr
CategoryID=1,
Price=54.99m,
Image="dragontouch.jpg"
},
newGadget{
Name="AlldaymallA88X7",
Description="ThisAlldaymalltabletfeaturingtheincred
CategoryID=1,
Price=47.99m,
Image="Alldaymall.jpg"
},
newGadget{
Name="ASUSMeMO",
Description="Pad7ME170CXA1BK7Inch16GBTablet.Dua
CategoryID=1,
Price=94.99m,
Image="asusmemo.jpg"
},
//Codeommitted

74
};
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

5/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

74
75
76

};
}
}

IhaveommittedsomeoftheGadgesobjectsforbrevitybutyoucanalwaysdownloadthesolution
projectatthebottomofthispost.NowletscreatetheHeartofthisproject.Addafoldernamed
Infrastructure.InordertousetheRepositoryPatternintherightmanner,weneedtodefinea
welldesignedinfrastructure.Fromthebottomtothetopallinstanceswillbeavailablethrough
injectedinterfaces.Andthefirstinstancewewillrequire,guesswhat..willbeaninstanceofthe
StoreEntities .SoletscreateafactoryInterfaceresponsibletoinitializeinstancesofthisclass.
AddaninterfacenamedIDbFactoryintotheInfrastructurefolder.
1
2
3
4

publicinterfaceIDbFactory:IDisposable
{
StoreEntitiesInit();
}

YoucanseethatthisinterfaceinheritstheIDisposableone,sotheConcreteclassthatwill
implementtheIDbFactoryinterface,mustalsoimplementtheIDisposableone.Todothisina
cleanway,addaDisposableclassthatwillimplementtheIDisposableinterface.Thenanyclass
thatwillimplementtheIDbFactoryinterface,willjustwanttoinheritthisveryclass.
Disposable.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

publicclassDisposable:IDisposable
{
privateboolisDisposed;

~Disposable()
{
Dispose(false);
}

publicvoidDispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
privatevoidDispose(booldisposing)
{
if(!isDisposed&&disposing)
{
DisposeCore();
}

isDisposed=true;
}

//Ovveridethistodisposecustomobjects
protectedvirtualvoidDisposeCore()
{
}
}

https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

6/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

IhavehighlightedtheDisposeCorevirtualmethodcausethismethodwillmakeothersclasses
inheritthisone,todisposetheirownobjectsinthewaytheneedto.Nowaddtheimplementation
classoftheIDbFactoryinterface.
DbFactory.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

publicclassDbFactory:Disposable,IDbFactory
{
StoreEntitiesdbContext;

publicStoreEntitiesInit()
{
returndbContext??(dbContext=newStoreEntities());
}

protectedoverridevoidDisposeCore()
{
if(dbContext!=null)
dbContext.Dispose();
}
}

Itstimetocreateageneric IRepository interfacewherewewilldeclarethedefaultoperations


thatourrepositorieswillsupport.HereIaddedsomethatithoughtarethemostusedones,but
youcanextendthoseoperationsasyouwish.
IRepository.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

publicinterfaceIRepository<T>whereT:class
{
//Marksanentityasnew
voidAdd(Tentity);
//Marksanentityasmodified
voidUpdate(Tentity);
//Marksanentitytoberemoved
voidDelete(Tentity);
voidDelete(Expression<Func<T,bool>>where);
//Getanentitybyintid
TGetById(intid);
//Getanentityusingdelegate
TGet(Expression<Func<T,bool>>where);
//GetsallentitiesoftypeT
IEnumerable<T>GetAll();
//Getsentitiesusingdelegate
IEnumerable<T>GetMany(Expression<Func<T,bool>>where);
}

NoticethattheCRUDoperationsarecommentedasMarktodosomething...Thismeansthat
whenarepositoryimplentationadds,updatesorremovesanentity,doesnotsendthecommand
tothedatabaseatthatverymoment.Instead,thecaller(servicelayer)willberesponsibletosend
aCommitcommandtothedatabasethroughaIUnitOfWorkinjectedinstance.Forthistobedone
willuseapatterncalledUnitOfWork.AddthefollowingtwofilesintotheInfrastructurefolder.
IUnitOfWork.cs
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

7/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

1
2
3
4

publicinterfaceIUnitOfWork
{
voidCommit();
}

UnitOfWork.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

publicclassUnitOfWork:IUnitOfWork
{
privatereadonlyIDbFactorydbFactory;
privateStoreEntitiesdbContext;

publicUnitOfWork(IDbFactorydbFactory)
{
this.dbFactory=dbFactory;
}

publicStoreEntitiesDbContext
{
get{returndbContext??(dbContext=dbFactory.Init());}
}

publicvoidCommit()
{
DbContext.Commit();
}
}

InthesamewayweusedtheDisposableclasswearegoingtouseanabstractclassthathas
virtualimplementationsofthe IRepository interface.Thisbaseclasswillbeinheritedfromall
specificrepositoriesandhencewillimplementtheIRepositoryinterface.Addthefollowingclass.
RepositoryBase.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

publicabstractclassRepositoryBase<T>whereT:class
{
#regionProperties
privateStoreEntitiesdataContext;
privatereadonlyIDbSet<T>dbSet;

protectedIDbFactoryDbFactory
{
get;
privateset;
}

protectedStoreEntitiesDbContext
{
get{returndataContext??(dataContext=DbFactory.Init());}
}
#endregion

protectedRepositoryBase(IDbFactorydbFactory)
{
DbFactory=dbFactory;
dbSet=DbContext.Set<T>();

23
}
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

8/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

#regionImplementation
publicvirtualvoidAdd(Tentity)
{
dbSet.Add(entity);
}

publicvirtualvoidUpdate(Tentity)
{
dbSet.Attach(entity);
dataContext.Entry(entity).State=EntityState.Modified;
}

publicvirtualvoidDelete(Tentity)
{
dbSet.Remove(entity);
}

publicvirtualvoidDelete(Expression<Func<T,bool>>where)
{
IEnumerable<T>objects=dbSet.Where<T>(where).AsEnumerable();
foreach(Tobjinobjects)
dbSet.Remove(obj);
}

publicvirtualTGetById(intid)
{
returndbSet.Find(id);
}

publicvirtualIEnumerable<T>GetAll()
{
returndbSet.ToList();
}

publicvirtualIEnumerable<T>GetMany(Expression<Func<T,bool>>where
{
returndbSet.Where(where).ToList();
}

publicTGet(Expression<Func<T,bool>>where)
{
returndbSet.Where(where).FirstOrDefault<T>();
}

#endregion

Sincetheimplementationsmarkedasvirtual,anyrepositorycanovverideaspecificoperationas
required.AndnowtheConcreterepositories:Addanewfoldernamed Repositories andaddthe
followingtwoclasses:
GadgetRepository.cs
1
publicclassGadgetRepository:RepositoryBase<Gadget>,IGadgetRepository
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/
9/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

1
2
3
4
5
6
7
8
9
10

publicclassGadgetRepository:RepositoryBase<Gadget>,IGadgetRepository
{
publicGadgetRepository(IDbFactorydbFactory)
:base(dbFactory){}
}

publicinterfaceIGadgetRepository:IRepository<Gadget>
{

CategoryRepository.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

publicclassCategoryRepository:RepositoryBase<Category>,ICategoryRepositor
{
publicCategoryRepository(IDbFactorydbFactory)
:base(dbFactory){}

publicCategoryGetCategoryByName(stringcategoryName)
{
varcategory=this.DbContext.Categories.Where(c=>c.Name==cate

returncategory;
}

publicoverridevoidUpdate(Categoryentity)
{
entity.DateUpdated=DateTime.Now;
base.Update(entity);
}
}

publicinterfaceICategoryRepository:IRepository<Category>
{
CategoryGetCategoryByName(stringcategoryName);
}

YoucanseethattheGadgetRepositorysupportsthedefaultoperationsusingthedefaultbehavior
andofcoursethatsOK.Ontheotherhand,youcanseeanexamplewhereaspecificrepository
requirestoeitherextenditsoperations(GetCategoryByName)oroverriedthedefaultones
(Update).UsuallyyouaddarepositoryforeachofyourModelclasses,henceeachrepositoryof
typeT,isresponsibletomanipulateaspecificDbSetthroughtheDbContext.Set<T>.Wearedone
implementingtheDataAccesslayersowecanprocceedtothenextone.

ServiceLayer
WhatoperationsdoyouwanttoexposeyourMVCControllers?Whereisthebusinesslogicis
goingtobeimplemented?Yeap..youhaveguessedright,inthisverylayer.Addanewclass
libraryprojectnamed Store.Service .Youwillhavetoaddreferencestothetwopreviouscreated
projects,Store.ModelandStore.Data.NoticeIhaventtoldyouyettoinstallEntityFrameworkto
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

10/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

thisproject..andIam
notgoingto,causeany
databaseoperation
requiredwillbedone
throughtheinjected
repositorieswecreated
before.Addthefirst
Servicefiletothis
project.

GadgetService.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

//operationsyouwanttoexpose
publicinterfaceIGadgetService
{
IEnumerable<Gadget>GetGadgets();
IEnumerable<Gadget>GetCategoryGadgets(stringcategoryName,string
GadgetGetGadget(intid);
voidCreateGadget(Gadgetgadget);
voidSaveGadget();
}

publicclassGadgetService:IGadgetService
{
privatereadonlyIGadgetRepositorygadgetsRepository;
privatereadonlyICategoryRepositorycategoryRepository;
privatereadonlyIUnitOfWorkunitOfWork;

publicGadgetService(IGadgetRepositorygadgetsRepository,ICategoryRep
{
this.gadgetsRepository=gadgetsRepository;
this.categoryRepository=categoryRepository;
this.unitOfWork=unitOfWork;
}

#regionIGadgetServiceMembers

publicIEnumerable<Gadget>GetGadgets()
{
vargadgets=gadgetsRepository.GetAll();

29
returngadgets;
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

11/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

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
54
55
56

returngadgets;
}

publicIEnumerable<Gadget>GetCategoryGadgets(stringcategoryName,
{
varcategory=categoryRepository.GetCategoryByName(categoryName);
returncategory.Gadgets.Where(g=>g.Name.ToLower().Contains(gadge
}

publicGadgetGetGadget(intid)
{
vargadget=gadgetsRepository.GetById(id);
returngadget;
}

publicvoidCreateGadget(Gadgetgadget)
{
gadgetsRepository.Add(gadget);
}

publicvoidSaveGadget()
{
unitOfWork.Commit();
}

#endregion

ThefirstandthelasthighlightedcodelinesremindsyouwhywecreatedtheIUnitOfWork
interface.Ifwewantedtocreateagadgetobjectthoughthisserviceclass,wewouldwrite
somethinglikethis..
1
2
3

//initagadgetobject..
gadgetService.CreateGadget(gadget);
gadgetService.SaveGadget();

Theotherhighlightedcodelinesdenotesthatanyrequiredrepositoryforthisservice,willbe
injectedthroughitsconstructor.Thiswillbedonethrougha DependencyContainer wewillsetup
intheMVCprojectsstartupclass,usingtheAutofacframework.InthesamewayIcreatedthe
GadgetService.csfile.
CategoryService.cs
1
//operationsyouwanttoexpose
2
publicinterfaceICategoryService
3
{
4
IEnumerable<Category>GetCategories(stringname=null);
5
CategoryGetCategory(intid);
6
CategoryGetCategory(stringname);
7
voidCreateCategory(Categorycategory);
8
voidSaveCategory();
9
}
10

11
publicclassCategoryService:ICategoryService
12
{
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

12/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

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
54
55

{
privatereadonlyICategoryRepositorycategorysRepository;
privatereadonlyIUnitOfWorkunitOfWork;

publicCategoryService(ICategoryRepositorycategorysRepository,IUnitOf
{
this.categorysRepository=categorysRepository;
this.unitOfWork=unitOfWork;
}

#regionICategoryServiceMembers

publicIEnumerable<Category>GetCategories(stringname=null)
{
if(string.IsNullOrEmpty(name))
returncategorysRepository.GetAll();
else
returncategorysRepository.GetAll().Where(c=>c.Name==name);
}

publicCategoryGetCategory(intid)
{
varcategory=categorysRepository.GetById(id);
returncategory;
}

publicCategoryGetCategory(stringname)
{
varcategory=categorysRepository.GetCategoryByName(name);
returncategory;
}

publicvoidCreateCategory(Categorycategory)
{
categorysRepository.Add(category);
}

publicvoidSaveCategory()
{
unitOfWork.Commit();
}

#endregion
}

Andwearedonewiththeservicelayeraswell.Letsprocceedwiththelastone,theASP.NET
MVCWebApplication.

PresentationLayer
AddanewASP.NETWebApplicationnamed Store.Web choosingtheemptytemplatewiththe
MVCoptionchecked.Weneedtoaddreferencestoallofthepreviousclasslibraryprojectsand
anEntityFrameworkinstallationviaNugetPackagesaswell.Youmaybewondering,arewe
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

13/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

goingtowriteany
EntityFramework
relatedqueriesinthis
project?Notatall,we
needthoughsomeof
itsnamespacessowe
cansetupthe
database
configurationsforour
application,suchas
the database
initializer .Andsince
westartedwiththis,
openGlobal.asax.cs
fileandaddthe
followinglineofcode
tosetuptheseed
initializerwecreatedin
theStore.Dataproject.
Glbal.asax.cs
1
2
3
4
5
6
7
8

protectedvoidApplication_Start()
{
//Initdatabase
System.Data.Entity.Database.SetInitializer(newStoreSeedData());

AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}

Youwillalsoneedtocreateaconnectionstringelementtodefinewhereyouwantthedatabaseto
becreated.AddthefollowingelementintheWeb.configfileandchangeditaccoardingtoyour
developmentenviromentrequirements.
Web.config
1
2
3

<connectionStrings>
<addname="StoreEntities"connectionString="DataSource=(localdb)\v11.0;Initi
</connectionStrings>

Wehavemadesuchaneffortinthepreviousstepstocreaterepositoriesandservicesbutnowits
thetimetomakethemworkalltogether.Ifyourecall,allservicescontructorshaverepositories
interfacesthatmustbeinjectedto.Theservicesthemeselfswilllaterbeinjectedintothe
controllersconustructorsandthisishowourapplicationwillwork.Toachievethis,weneedto
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/
14/30
setup DependancyInjection andforthishreasonIdecidedtouseAutofac.Makesureyouinstall

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

setup DependancyInjection andforthishreasonIdecidedtouseAutofac.Makesureyouinstall


AutofacASP.NETMVC5Integration throughNugetPackages.

CreateaBootstrapper.csfileundertheStart_Appfolderandpastethefollowingcode.
Bootstrapper.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

publicstaticvoidRun()
{
SetAutofacContainer();
}

privatestaticvoidSetAutofacContainer()
{
varbuilder=newContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRe
builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerRequ

//Repositories
builder.RegisterAssemblyTypes(typeof(GadgetRepository).Assembly)
.Where(t=>t.Name.EndsWith("Repository"))
.AsImplementedInterfaces().InstancePerRequest();
//Services
builder.RegisterAssemblyTypes(typeof(GadgetService).Assembly)
.Where(t=>t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();

IContainercontainer=builder.Build();
DependencyResolver.SetResolver(newAutofacDependencyResolver(conta
}

25
}
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

15/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

25

Thecodeitselfisselfexplanatory.Ihopeyouhavefollowedalongwithmeandyouhavenamed
yourrepositoryandserviceclassesasIdid,causeotherwise,thisisnotgonnawork.Thereare
twoimportantsthingslefttocompletethetutotiral.ThefirstonetodefineViewModelclassesand
set Automapper tomapdomainentitiestoviewmodelsandbackwards.Thesecondoneistosee
howtosetupCSSBootstrapinourwebapplication.Isupposemostofyou,installbootstrapfrom
NugetPackagesandstartaddingcssandscriptreferencestoyourproject.Herethoughwewill
followadifferentapproach.

CSSBootstrap
FirstofalldownloadBoostrapdistributionfromtheofficialsite.Addthreefolderstoyour
applicationnamedcss,fontsandjsrespectively.Inthecssfolderpastethebootstrap.cssfilefrom
whatyouhavedownloaded,inthefontsfolderpasteeverythingisinsidetherespectivefonts
folderandinthejsfolder,justpastethebootstrap.jsfile.WearegoingtouseBundlingand
Minificationforbootstrapandtoachievethatyouneedtoinstall MicrosoftASP.NETWeb
OptimazationFramework throughNugetPackages.
Whenyoufinishinstallingthis,addanewclassnamedBundleConfigintotheApp_Startfolderas
follow:
BundleConfig.cs
1
2
3
4
5
6
7
8
9
10

publicclassBundleConfig
{
publicstaticvoidRegisterBundles(BundleCollectionbundles)
{
bundles.Add(newScriptBundle("~/bootstrap/js").Include("~/js/boots
bundles.Add(newStyleBundle("~/bootstrap/css").Include("~/css/boot

BundleTable.EnableOptimizations=true;
Follow
}
}

Follow chsakell's
Blog

AsyoucanseeIhavealsoreferencedsite.jsandsite.cssjavascriptandcssfiles.Thosefilescan
Get every new post delivered to
hostanybootstrapcsscustomazationsyouwannadooranyjavascriptrelatedcode.Feelfreeto
your Inbox.
addtherespectivefilesandleavethemempty.NowweneedtodeclarethatwewantMVCtouse
Join 562 other followers
bundlingandminication,soaddthefollowinglineintotheGlobal.asax.csfile.
Enter your email address

Sign me up
Build a website with WordPress.com

https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

16/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

Global.asax.cs
1
2
3
4
5
6
7
8
9
10
11
12

protectedvoidApplication_Start()
{
//Initdatabase
System.Data.Entity.Database.SetInitializer(newStoreSeedData());

AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);

//AutofacandAutomapperconfigurations
Bootstrapper.Run();
Follow
}

Follow chsakell's

NoticethatIhavealsocalledtheBootstrapper.Run()functionthatwillsetuptheAutofacs
Blog
configurationwemadebefore.ThisfunctionwillalsoconfigureAutomapper,somethingwegonna
Get every new post delivered to
seeinabit.LetsfinishwithBootrapfornow.WewillneedaLayouttouseforourapplication,so
your Inbox.
goandcreateaSharedfolderundertheViewsfolderandaddanewitemoftype
MVC5Layout
Join 562 other followers
Page(Razor) named_Layout.cshtml.

_Layout.cshtml
1
2
3
4
5
6

Enter your email address

<!DOCTYPEhtml>
Sign me up
<html>
<head>
Build a website with WordPress.com
<metacharset="utf8">
<metahttpequiv="XUACompatible"content="IE=edge">
<metaname="viewport"content="width=devicewidth,initialscale=1">

7
<title>@ViewBag.Title</title>
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

17/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

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

<title>@ViewBag.Title</title>
<!Bootstrap>
@Styles.Render("~/bootstrap/css")
<![ifltIE9]>
<scriptsrc="https://oss.maxcdn.com/libs/html5shiv/3.7.0/
html5shiv.js"></script>
<scriptsrc="https://oss.maxcdn.com/libs/respond.js/1.4.2/
respond.min.js"></script>
<![endif]>
</head>
<body>
<navid="myNavbar"class="navbarnavbardefaultnavbarinversenavbarfixe
<!Brandandtogglegetgroupedforbettermobiledisplay>
<divclass="navbarheader">
<buttontype="button"class="navbartoggle"datatoggle="colla
<spanclass="sronly">Togglenavigation</span>
<spanclass="iconbar"></span>
<spanclass="iconbar"></span>
<spanclass="iconbar"></span>
</button>
@Html.ActionLink("Store","Index","Home",new{},new{@cla
</div>
<!Collectthenavlinks,forms,andothercontentfortoggling
<divclass="collapsenavbarcollapse"id="navbarCollapse">
<ulclass="navnavbarnav">
<liclass="active">
@Html.ActionLink("Tablets","Index","Home",new{cat
</li>
<liclass="active">
@Html.ActionLink("Laptops","Index","Home",new{cat
</li>
<liclass="active">
@Html.ActionLink("Mobiles","Index","Home",new{cat
</li>
</ul>
</div>
</nav>
@RenderBody()
<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js
Follow
@Scripts.Render("~/bootstrap/js")
</body>
</html>
Follow chsakell's

Blog

ThepagewillprobablycomplainthatcannotresolveRazorsyntaxsoyouhavetoaddthe

Get every new post delivered to

followingusingstatementintheweb.configfilewhichisundertheViewsfolder(notapplications
your Inbox.
web.config).Followingispartofthatfile..
Join 562 other followers

web.config
1
2
3
4
5
6
7

Enter your email address

<namespaces>
<addnamespace="System.Web.Mvc"/>
Sign me up
<addnamespace="System.Web.Mvc.Ajax"/>
<addnamespace="System.Web.Mvc.Html"/>
Build a website with WordPress.com
<addnamespace="System.Web.Routing"/>
<addnamespace="Store.Web"/>
<addnamespace="System.Web.Optimization"/>

8
</namespaces>
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

18/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

</namespaces>

Automapper
Inarealapplicationyourdomainobjectswillprobablyhavealotofpropertiesbutyouonlywantto
displaysomeoftheminthebrowser.Moreoverwhenpostingbacktoserver,forexamplewhen
creatingobjectsthoughaformelement,youalsowanttopostonlyafewofthedomainobjects
properties.ForthisreasonyoudefineViewModelobjectsandusetheminsteadofthereal
domainones.MakesureyouinstallAutomapperfromNugetPackages.

AddanewfoldernamedViewModelswiththefollowingclasses.
GadgetViewModel.cs
1
2
3
4
5
6
7
8
9
10

publicclassGadgetViewModel
{
publicintGadgetID{get;set;}
publicstringName{get;set;}
publicstringDescription{get;set;}
publicdecimalPrice{get;set;}
publicstringImage{get;set;}

publicintCategoryID{get;set;}
}

Follow

Follow chsakell's
Blog
Get every new post delivered to
your Inbox.
Join 562 other followers
Enter your email address

Sign me up

CategoryViewModel.cs
1
2
3

publicclassCategoryViewModel
{
publicintCategoryID{get;set;}

4
publicstringName{get;set;}
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

Build a website with WordPress.com

19/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

4
5
6
7

publicstringName{get;set;}

publicList<GadgetViewModel>Gadgets{get;set;}
}

GadgetFormViewModel.cs
1
2
3
4
5
6
7
8

publicclassGadgetFormViewModel
{
publicHttpPostedFileBaseFile{get;set;}
publicstringGadgetTitle{get;set;}
publicstringGadgetDescription{get;set;}
publicdecimalGadgetPrice{get;set;}
publicintGadgetCategory{get;set;}
}

WhenyourViewModelclasseshavepropertiesnamedastherespectivedomainobjects,
Automapperissmartenoughtomakethemappingthroughdefaultconventions.Otherwiseyou
havetosetthemappingmanualybyyourself.NoticethelastclassIhaveadded,the
GadgetFormViewModel.WecanmakeaconvetiontoaddaFormwordbeforeViewModelso
thatweknowthatthistypeofviewmodel,ispostedbacktoserverthroughaformelement.Lets
nowconfigurethemappings.Addanewfolder Mappings andaddthefollowingclassfile.
AutoMapperConfiguration.cs
1
2
3
4
5
6
7
8
9
10
11

publicclassAutoMapperConfiguration
{
publicstaticvoidConfigure()
{
Mapper.Initialize(x=>
{
x.AddProfile<DomainToViewModelMappingProfile>();
x.AddProfile<ViewModelToDomainMappingProfile>();
});
}
}

Follow
Wehaventcreatedtherequiredprofilesyetbutwewillinabit.WhatIwantedtoshowyouisthat
youcancreateasmanyAutomapperprofilesyouwantandthenaddthemintoMapper.Initialize
Follow chsakell's
function.Herewewilldefinetwoprofiles,onetomapdomainmodelstoViewModelsandanother
Blog
oneforbackwards.Addthefollowingclassesinthesamefolderastheprevious.

DomainToViewModelMappingProfile.cs

Get every new post delivered to


your Inbox.

Join 562 other followers


1
publicclassDomainToViewModelMappingProfile:Profile
2
{
Enter your email address
3
publicoverridestringProfileName
4
{
5
get{return"DomainToViewModelMappings";}
Sign me up
6
}
7

8
protectedoverridevoidConfigure()
Build a website with WordPress.com
9
{
10
Mapper.CreateMap<Category,CategoryViewModel>();
11
Mapper.CreateMap<Gadget,GadgetViewModel>();
12
}
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

20/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

12
13

}
}

ViewModelToDomainMappingProfile.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

publicclassViewModelToDomainMappingProfile:Profile
{
publicoverridestringProfileName
{
get{return"ViewModelToDomainMappings";}
}

protectedoverridevoidConfigure()
{
Mapper.CreateMap<GadgetFormViewModel,Gadget>()
.ForMember(g=>g.Name,map=>map.MapFrom(vm=>vm.GadgetTitl
.ForMember(g=>g.Description,map=>map.MapFrom(vm=>vm.Gad
.ForMember(g=>g.Price,map=>map.MapFrom(vm=>vm.GadgetPri
.ForMember(g=>g.Image,map=>map.MapFrom(vm=>vm.File.File
.ForMember(g=>g.CategoryID,map=>map.MapFrom(vm=>vm.Gadg
}
}

FortheDomain>ViewModelsmappingwedidntneedtosetupanything.Automapperwilluse
thedefaultconventionsandthatsfine.ForourGadgetFormViewModel>Gadgetmapping
though,wesetmanuallytheconfigurationasshownabove.Thelastthingremainedtofinishwith
AutomapperistoaddthefollowinglineintheBootstrapperclass.
Bootsrapper.cs
1
2
3
4
5
6
7
8
9

publicstaticclassBootstrapper
{
publicstaticvoidRun()
{
SetAutofacContainer();
//ConfigureAutoMapper
AutoMapperConfiguration.Configure();
}
//Codeommitted

ControllersandViews

Follow

Follow chsakell's
Blog

Get every new post delivered to


Wearealmostfinished.AddanewMVCControllernamedHomeControllerandpastethe
your Inbox.
followingcode.
Join 562 other followers

HomeController.cs

Enter your email address

1
publicclassHomeController:Controller
2
{
3
privatereadonlyICategoryServicecategoryService;
Sign me up
4
privatereadonlyIGadgetServicegadgetService;
5

Build a website with WordPress.com


6
publicHomeController(ICategoryServicecategoryService,IGadgetService
7
{
8
this.categoryService=categoryService;
9
this.gadgetService=gadgetService;
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/
21/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

this.gadgetService=gadgetService;
}

//GET:Home
publicActionResultIndex(stringcategory=null)
{
IEnumerable<CategoryViewModel>viewModelGadgets;
IEnumerable<Category>categories;

categories=categoryService.GetCategories(category).ToList();

viewModelGadgets=Mapper.Map<IEnumerable<Category>,IEnumerable<C
returnView(viewModelGadgets);
}
}

NowyoucanseeinactionwhywehavemadesuchanefforttosetupRepositories,Services,
AutofacandAutomapper.Serviceswillbeinjectedinthecontrollerforeachrequestandtheirdata
willbemappedtoViewModelsbeforesendtotheClient.RightclickintheIndexactionandadda
ViewnamedIndexwiththefollowingcode.Imustmentionhere,thatthegadgetsobjectsweuse,
haveimagereferencestoafoldernamedimagesintheWebApplicationproject.Youcanuse
yourimagesorjustdownloadthisprojectattheendofthispost.
Views/Home/Index.cshtml
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

@modelIEnumerable<Store.Web.ViewModels.CategoryViewModel>

@{
ViewBag.Title="Store";
Layout="~/Views/Shared/_Layout.cshtml";
}

<p>

</p>
<divclass="container">
Follow
<divclass="jumbotron">

@foreach(variteminModel)
Follow chsakell's
{
<divclass="panelpaneldefault"> Blog
<divclass="panelheading">
Get every new post delivered to
@*@Html.DisplayFor(modelItem=>item.Name)*@
your Inbox.
@Html.ActionLink("Viewall"+item.Name,"Index",new
@using(Html.BeginForm("Filter","Home",new{category=
Join 562 other followers
{
@Html.TextBox("gadgetName",null,new{@class=
Enter your email address
}

Sign me up
</div>
@foreach(vargadgetinitem.Gadgets)
Build a website with WordPress.com
{
@Html.Partial("Gadget",gadget)
}

31
<divclass="panelfooter">
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

22/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

<divclass="panelfooter">
@using(Html.BeginForm("Create","Home",FormMethod.Post,
new{enctype="multipart/formdata",@class
{
@Html.Hidden("GadgetCategory",item.CategoryID)
<divclass="formgroup">
<labelclass="sronly"for="file">File</label>
<inputtype="file"class="formcontrol"name=
</div>
<divclass="formgroup">
<labelclass="sronly"for="gadgetTitle">Title</la
<inputtype="text"class="formcontrol"name=
</div>
<divclass="formgroup">
<labelclass="sronly"for="gadgetName">Price</lab
<inputtype="number"class="formcontrol"name=
</div>
<divclass="formgroup">
<labelclass="sronly"for="gadgetName">Descriptio
<inputtype="text"class="formcontrol"name=
</div>
<buttontype="submit"class="btnbtnprimary">Upload</
}
</div>
</div>
}

</div>

</div>

Twothingstonoticehere.ThefirstoneisthatweneedtocreateaPartialviewtodiplaya
GadgetViewModel objectandthesecondoneistheFormscontrolelementsnames.Youcan
seethattheymuchourGadgetFormViewModelproperties.UndertheSharedfoldercreatethe
followingPartialviewfordisplayingaGadgetViewModelobject.
Views/Shared/Gadget.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Follow

@modelStore.Web.ViewModels.GadgetViewModel
Follow chsakell's

<divclass="panelbody">
Blog
<divclass="media">
<aclass="pullleft"href="#">
Get every new post delivered to
<imgclass="mediaobject"src="../../images/@Model.Image"/>
your Inbox.
</a>
Join 562 other followers
<divclass="mediabody">
<h3class="mediaheading">
@Model.Name
Enter your email address
</h3>
<p>@Model.Description</p>
Sign me up
</div>
</div>
</div>
Build a website with WordPress.com

https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

23/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

IntheIndex.cshtmlpageIhaveaddedsearchandfilterfunctionalityandCreategadgetaswell.
ToachievethatyouneedtoaddthefollowingActionmethodstotheHomeController.
HomeController.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

publicActionResultFilter(stringcategory,stringgadgetName)
{
IEnumerable<GadgetViewModel>viewModelGadgets;
IEnumerable<Gadget>gadgets;

gadgets=gadgetService.GetCategoryGadgets(category,gadgetName);

viewModelGadgets=Mapper.Map<IEnumerable<Gadget>,IEnumerable<Gad

returnView(viewModelGadgets);
}

[HttpPost]
publicActionResultCreate(GadgetFormViewModelnewGadget)
{
if(newGadget!=null&&newGadget.File!=null)
{
vargadget=Mapper.Map<GadgetFormViewModel,Gadget>(newGadget
gadgetService.CreateGadget(gadget);

stringgadgetPicture=System.IO.Path.GetFileName(newGadget.Fi
stringpath=System.IO.Path.Combine(Server.MapPath("~/images/
newGadget.File.SaveAs(path);

gadgetService.SaveGadget();
}

varcategory=categoryService.GetCategory(newGadget.GadgetCategor
returnRedirectToAction("Index",new{category=category.Name})
}

IamsurethatatthispointyouunderstandthepurposeofalltheabovecodesoIwontexplain
Follow
anything.YouneedtoaddaFilterpagesorightclickintheFilteractionandcreatethefollowing
View.
Home/Views/Filter.cshtml

Follow chsakell's
Blog

Get every new post delivered to


1
@modelIEnumerable<Store.Web.ViewModels.GadgetViewModel>
2

your Inbox.
3
@{
Join 562 other followers
4
ViewBag.Title="Filter";
5
Layout="~/Views/Shared/_Layout.cshtml";
Enter your email address
6
}
7

8
<divclass="container">
Sign me up
9
<divclass="jumbotron">
10

11
@foreach(variteminModel)
Build a website with WordPress.com
12
{
13
<divclass="panelpaneldefault">
14
<divclass="panelheading">
15
@Html.Label(item.Name)
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

24/30

7/8/2016

14
15
16
17
18
19
20
21
22

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog
<divclass="panelheading">
@Html.Label(item.Name)
</div>
@Html.Partial("Gadget",item)
</div>
}

</div>
</div>

Youcanfiltergadgetsbycategoryorsearchgadgetsinaspecificcategory.Thatsit,wehave
finishedcreatingahighlydecoupledarchitecturethatcouldsupportlargescaleMVCapplications.

Github
IhavedecidedtomoveallofmycodetomyGithubaccountsoyoucandownloadmostofthe
projectsthatwehaveseenonthisblogfromthere.YoucandownloadthisprojectfromhereFollow
.I
hopeyouenjoyedthispostasmuchasIdid.

Follow chsakell's

Incaseyoufindmyblogscontentinteresting,registeryouremailtoreceivenotificationsofnew
Blog
postsandfollowchsakellsBlogonitsFacebookorTwitteraccounts.
Facebook

Twitter

Get every new post delivered to


your Inbox.
Join 562 other followers
Enter your email address

Sign me up
Build a website with WordPress.com

https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

25/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

.NETWebApplicationDevelopmentbyChrisS.

Abouttheseads

96replies
Lang
April18,20165:07pm

Thankyouforthisarticle.
Ihaveaquestion.Inservicelayer,RepositoryandUnitOfWorkhavetheirowndbContext,sohowcan
theUnitOfWorkcommitthesessionthatwashandledbytheRepository?

ChristosS.
April18,20165:34pm

Follow
Goodquestion.ItsactuallythesamedbContextinstancethatcomesfromtheuniqueinjected
DbFactoryinstanceperrequest.ChearstoAutofac!
partofBootstrapper.cs
1
2
3
4

Follow chsakell's
Blog

Get every new post delivered to


varbuilder=newContainerBuilder();
your Inbox.
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
Join 562 other followers
builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerRequest();
Enter your email address

Lang
April19,20162:12am

Sign me up
Build a website with WordPress.com

Thanksalot,nowIunderstand.Haveanicedaysir.
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

26/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

subramanihm
May22,20166:54pm

Verynicearticle

SarthakKilledar
June15,20165:49pm

Wasscratchingmyheadoverthisfortwodays,forgottoaddInstancePerRequestinmy
project.Thanksfortheclarification.

zicki
April20,20164:17am

Thanksforyourawesomearticle!
ImcurrentlysettingupmythirdMVCProjectusingyourtutorial
itwasreallyeasytofollowyourguideandtoadaptitformyrequirements!

DavidCherian
May4,20166:56pm

Thanksalotforthisexcellentarticle.

Follow

Follow chsakell's
Blog

IllbeusingthisarchitecturestyleinmynextMVCprojectreallycommittedtoyouforsharingthis
sample.

Get every new post delivered to


your Inbox.

TomFrajer
May5,20168:58am

CanIuseDapperinsteadofEntityFrameworkinthisarchitecture?

ChristosS.
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

Join 562 other followers


Enter your email address

Sign me up
Build a website with WordPress.com

27/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

May5,20169:08am

Ofcourse.

Viethoang
May6,20164:40am

Hi,
IhaveaquestionaboutDI.
Howtoknowwhichclasseswillbeinjectedtheobjects?Weneedtoconfigthisorallofclassesthat
hasconstructorwithregisteredobjectparameterwillbeinjected?

ArashMotamedi
May7,20167:46pm

Greatpost!ThankyouChristos.Onesuggestion:tookmealittlewhiletofigureoutwhymyWebApi
controllerswerethrowingadefaultconstructornotfoundexceptionandrealizethataseparate
Autofacdependencyandacoupleextralinesofconfigurationwereneeded.Maybemention
somewherethatifthedevlikestoincludeWebApicontrollersintheirproject,theyneedtoreference
Autofac.WebApi2andthenintheBootstrapper,RegisterApiControllers()andalsosetthe
GlobalConfiguration.Configuration.DependencyResolvertoAutofacWebApiresolver.

AmitJain
May11,201610:06am

Reallyagreatarticle!!

AmitJain
May11,201610:16am

Follow

Follow chsakell's
Blog
Get every new post delivered to
your Inbox.
Join 562 other followers

Fortheicingonthecake,wecandosmalladditiontothissolutioni.e.DatahidingApproach.Wecan
hidethereferenceofModelsintheWebProject

Enter your email address

Sign me up

Mathias

Build a website with WordPress.com

May23,20165:49pm
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

28/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

Simplybrilliant.Agoodwayfornewcommerstoasp.nettodiveintoarchitectures!Thankyou!

hove1543
May23,20165:52pm

Brilliant!Thankyouverymuchforthisguide!Greatfornewcomers(asmyself)tolearnmoreabout
architectinganasp.netmvcapp.

bhavesh
May25,20162:37pm

NiceArticleeasytounderstand

ctysingh
May30,20169:05pm

Rebloggedthisonchaitanyasingh.

Manoj
June4,201612:45pm

Thankyousirforthisarticle.Reallyagreatarticle.Itiseasytounderstand
Follow

MohammedNazimFeroz
June6,20163:17pm

HiChristos,

Follow chsakell's
Blog
Get every new post delivered to

Thanksforthisamazingpost!Canyoupleaseletmeknowifyouhaveanexampleofusing
your Inbox.
FluentValidationontheFormViewModelclasses?Thanks.

Join 562 other followers


Enter your email address

Nisha

Sign me up

June9,20166:52am

Canyoupleaseshare,howtowritetestcasesforthisarchitecture.

https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

Build a website with WordPress.com

29/30

7/8/2016

ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog

ggagnaux
June25,20166:45pm

Wellwrittenarticle!Thanksforsharing.IllneedtoexaminemuchmorecloselywhenIhavesometime

KeithHarris
July5,20164:22am

ThisisbyfarthemostthoroughandcompletetutorialonRepository/UnitofWorkpatternIveseenon
theweb.AllotherexamplesIveseenarepartialanddontgointootherareasbeyondthe
infrastructure.IveheardofAutofac/Automapperbefore,butneversawthemasthegluetomakeitall
workbecausetheywerenevercoveredthisway.Now,ifyouwouldonlyincludeaDomainDriven
Designpattern,Iwouldbein7thHeaven,lol

minhpn
July5,20164:50am

thanksthearticle!howtocreatekeyattributeindentityinmodel?
OlderComments

Trackbacks
1.AspNetMvcArchitectureBestPractices|NewArchitectureFan
Follow

Follow chsakell's
Blog
Get every new post delivered to
your Inbox.
Join 562 other followers
Enter your email address

Sign me up
Build a website with WordPress.com

https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/

30/30

Vous aimerez peut-être aussi