MVC Architecture
MVC Architecture
ASP.NETMVCSolutionArchitectureBestPracticeschsakell'sBlog
chsakell's Blog
ANYTHING AROUND ASP.NET MVC,WEB API, WCF, ENTITY
FRAMEWORK & ANGULARJS
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();
}
}
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
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
<!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
followingusingstatementintheweb.configfilewhichisundertheViewsfolder(notapplications
your Inbox.
web.config).Followingispartofthatfile..
Join 562 other followers
web.config
1
2
3
4
5
6
7
<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/
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
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
HomeController.cs
1
publicclassHomeController:Controller
2
{
3
privatereadonlyICategoryServicecategoryService;
Sign me up
4
privatereadonlyIGadgetServicegadgetService;
5
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
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
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
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.
TomFrajer
May5,20168:58am
CanIuseDapperinsteadofEntityFrameworkinthisarchitecture?
ChristosS.
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/
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
Sign me up
Mathias
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.
Nisha
Sign me up
June9,20166:52am
Canyoupleaseshare,howtowritetestcasesforthisarchitecture.
https://chsakell.com/2015/02/15/aspnetmvcsolutionarchitecturebestpractices/
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