https://www.codeproject.com/Articles/1162975/Bootstrapper-Loader-for-Layered-Architecture
-
Build the solution (make sure your machine has .NET Core - Version 2.0.0):
dotnet build
-
Make sure you have PostgreSQL database running. If you want to use Docker, here is the command to create a PostgreSQL container for testing purpose:
docker run --name bootstrapper-loader-demo -p 5432:5432 -e POSTGRES_PASSWORD=mysecretpassword -d postgres
-
Update connection string in
BootstrapperLoaderDemo\appsettings.json
to correct database connection string (The connection string provided inappsettings.json
assumes we are using PostgreSQL database created from docker command above) -
Run
BootstrapperLoaderDemo
dotnet run
If everything works correctly, it should show this home page:
BootstrapperLoaderDemo
: main UI project, contains a singleHomeController
that displays a list of books retrieved from databaseBootstrapperLoaderDemo.Core
: contain domain model (Book
) and repository interface (IBookRepository
)BootstrapperLoaderDemo.Repository
: contains implementation of book repository (BookRepository
)
-
There's no project reference from
BootstrapperLoader
toBootstrapperLoaderDemo.Repository
. Controllers work strictly with repository interfaces (fromCore
project) -
Since there's no project reference from
BootstrapperLoader
toBootstrapperLoaderDemo.Repository
, Visual Studio will not copyBootstrapperLoaderDemo.Repository.dll
and its dependencies to output directory ofBootstrapperLoader
during building. To work around this, I usePostBuild
target inBootstrapperLoaderDemo.csproj
:<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <ItemGroup> <ItemsToCopy Include="./../BootstrapperLoaderDemo.Repository/bin/$(Configuration)/$(TargetFramework)/*.dll" /> </ItemGroup> <Copy SourceFiles="@(ItemsToCopy)" DestinationFolder="./bin/$(Configuration)/$(TargetFramework)/temp/"> <Output TaskParameter="CopiedFiles" ItemName="SuccessfullyCopiedFiles"/> </Copy> <Message Importance="High" Text="PostBuild Target successfully copied:%0a@(ItemsToCopy->'- %(fullpath)', '%0a')%0a -> %0a@(SuccessfullyCopiedFiles->'- %(fullpath)', '%0a')"/> </Target>
-
BootstrapperLoaderDemo.Repository
is responsible for its own initialization (inBootstrapper
class):- It registers concrete implementation of repositories to their interface in
Bootstrapper.ConfigureContainer()
- It does database initialization (drop and create database, create tables, seeds data) in
Bootstrapper.ConfigureDevelopment()
- It registers concrete implementation of repositories to their interface in
-
BootstrapperLoader
object is created and configured inBootstrapperLoaderDemo.Startup
constructor:_bootstrapperLoader = new LoaderBuilder() //Look for all dlls starting with BootstrapperLoaderDemo .Use(new FileSystemAssemblyProvider(PlatformServices.Default.Application.ApplicationBasePath, "BootstrapperLoaderDemo.*.dll")) .ForClass() //Inject Configuration object into Bootstrapper classes found in those dlls .HasConstructorParameter(Configuration) //Call Bootstrapper.ConfigureDevelopment() if it's development environment .When(env.IsDevelopment) .AddMethodNameConvention("Development") .Build();
It triggers ConfigureContainer()
in bootstrapper classes in Startup.ConfigureServices()
, passing in IServiceCollection
instance so that bootstrapper classes can register IoC mapping:
_bootstrapperLoader.TriggerConfigureContainer(services);
And trigger ConfigureDevelopment()
in bootstrapper classes in Startup.Configure()
, passing in IoC GetService()
:
// need to create scope during application startup due to: https://stackoverflow.com/questions/44180773/dependency-injection-in-asp-net-core-2-thows-exception
var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
_bootstrapperLoader.TriggerConfigure(scope.ServiceProvider.GetRequiredService);
}