I have very simple n-tier monolithic ASP.NET core project and try to setup in docker container but somehow it’s kept on failing and i have researched quite a bit unfortunately could not understand the docker file setup structure or process, it is quite interesting to see how project behave very strange.
Let me outline my project structure. A WebMVC project uses SQL Server as a database therefore localdb does not work in dockerize environment hence i had to pull sql sever container image from docker hub.
Dockerfile in MVC Project Root:-
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src
COPY ["src/Web/WebUI/WebUI.csproj", "src/Web/WebUI/"]
COPY ["src/Core/Application/Application.csproj", "src/Core/Application/"]
COPY ["src/Infrastructure/Persistence/Persistence.csproj", "src/Infrastructure/Persistence/"]
RUN dotnet restore "src/Web/WebUI/WebUI.csproj"
COPY . .
WORKDIR "/src/src/Web/WebUI"
RUN dotnet build "WebUI.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "WebUI.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "WebUI.dll"]
docker-compose.yml file structure:
version: '3.4'
services:
sqlserver.data:
image: mcr.microsoft.com/mssql/server:2017-latest
webui:
image: ${DOCKER_REGISTRY-}webui
build:
context: .
dockerfile: src/Web/WebUI/Dockerfile
depends_on:
- sqlserver.data
docker-compose-override.yml file structure:-
version: '3.4'
services:
sqlserver.data:
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=Pass@word
ports:
- "5433:1433"
webui:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- Connection=Server=sqlserver.data;Database=eWebShopDatabase;User=sa;Password=Pass@word
ports:
- "5107:80"
appsetting.json file to serve database connection:-
{
"Connection": "Server=sqlserver.data;Database=eWebShopDatabase;User=sa;Password=Pass@word;"
}
Sql Server database service registration:-
public static IServiceCollection AddSqlServerDatabase(this IServiceCollection services, IConfiguration configure)
{
services.AddEntityFrameworkSqlServer()
.AddDbContext<CatalogContext>(options =>
{
options.UseSqlServer(configure["Connection"],
sqlServerOptionsAction: resilient =>
{
//resilient.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
resilient.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
}, ServiceLifetime.Scoped);
services.AddEntityFrameworkSqlServer()
.AddDbContext<AppIdentityDbContext>(options =>
{
options.UseSqlServer(configure["Connection"],
sqlServerOptionsAction: resilient =>
{
resilient.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
}, ServiceLifetime.Scoped);
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
return services;
}
I have two dbcontext files and generated migrations accordingly, folder structures of these two migration files.BTW these two folders are located in Infrastructure layer of the project:-
Infrastructure
1. Auth
1. Migrations
1. Identitydbcontext
2. Migrations
2. Catalogdbcontext
Program:-
public async static Task Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
await Policy.Handle<Exception>()
.WaitAndRetry(9, r => TimeSpan.FromSeconds(5), (ex, ts) =>
{
Log.Error("Error connecting to SQL Server. Retrying in 5 sec.");
}).Execute(async () =>
{
var logger = services.GetService<ILogger<CatalogContextSeed>>();
var catalogContext = services.GetRequiredService<CatalogContext>();
await CatalogContextSeed.SeedAsync(catalogContext, logger);
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
var contextLogger = services.GetService<ILogger<AppIdentityDbContextSeed>>();
await AppIdentityDbContextSeed.SeedAsync(userManager, contextLogger);
});
}
catch (Exception ex)
{
var logger = services.GetService<ILogger<Program>>();
logger.LogError("Program could not seed the data and terminated unexpectedly", ex.Message);
}
}
host.Run();
}
Problem descriptions:-
docker-compose build : succeeded.
docker-compose up: could not create webui container from the image, however sql-server container running but docker compose showing error that webui could not connect to the sql server database which it depends on, therefore it seems to fail to create a container.
Now interesting part is:- open a new command prompt and try to run “docker-compose up” again and this time it connects to the database and created a database and seed the initial data into the database, but…It fails to create an identitydbcontext table in the database. This works fine when I connect it to local SQL Server Database. Therefore I assume it is a problem with docker-compose file setup. I tried researched on it quite a bit but could not understand docker-compose setup.
Now my questions is:-
- Why mvc project is not been able to connect to the sql database at first place, since it depends on SQL, I understand it takes time to spin up the database therefore i have setup initialized database resilient policy?
- Why another migration file is not been read to generate/create table into the database? specifically this Infrastructure/Auth/Migrations/Identitydbcontext, by the way it does not matter where the file is located, since i have changed the directory and it’s seem problem.
I am confused, since there was no problem running locally, therefore i suspect or assume it is something to do with docker-compose file setup, but i have watched/researched on this from Docker documentation and Microsoft docs as well but could not find any answers on this issue.
I am hoping someone from the community would help me.