1

I have a simple Entity Framework Core (5.0.11) application that connects with the localhost instance of the SQL Server. It works fine when I run from IDE but gives SQL connection error when I run from Docker Container.

I'm not using a stand alone container for the SQL server, rather connecting to the localhost on my machine.

Connection string I'm using is as follow

  "ConnectionStrings": {
    "DefaultConnectionString": "Server=localhost;Database=LOCALDatabase;User id=userId;password=password"
  }

Following is the docker container error

"EventId":20004,"LogLevel":"Error","Category":"Microsoft.EntityFrameworkCore.Database.Connection","Message":"An error occurred using the connection to database \u0027LOCALDatabase\u0027 on server \u0027localhost\u0027.","State":{"Message":"An error occurred using the connection to database \u0027LOCALDatabase\u0027 on server \u0027localhost\u0027.","database":"LOCALDatabase","server":"localhost","{OriginalFormat}":"An error occurred using the connection to database \u0027{database}\u0027 on server \u0027{server}\u0027."

I have tested the containerized application with dummy data (Not from the DB) and it is working fine so there is no issue in port mapping or accessibility of the containerized application.

Docker build and run commands are as follow

docker build -t test-api:v1 .

docker run -it -p 7277:80 --name testapi test-api:v1

Update

Following are the code files

//Data Access Layer Project
namespace DAL.Repositories
{
    public interface ITable1Repository
    {
        Task<IEnumerable<Dbtable1>> GetAsync();
    }
}

public class Table1Repository : ITable1Repository
{
    private readonly LOCALDbContext _dbContext;
    public Table1Repository(LOCALDbContext dbContext)
    {
        _dbContext = dbContext;
    }
    public async Task<IEnumerable<Dbtable1>> GetAsync()
    {
        return await _dbContext.Dbtable1s.ToListAsync();
    }
}

//Database entity
namespace DAL.Entities
{
    [Table("DBTable1")]
    public partial class Dbtable1
    {
        [Key]
        public int Column1 { get; set; }
        [StringLength(50)]
        public string Column2 { get; set; }
        [StringLength(50)]
        public string Column3 { get; set; }
    }
}

//DbContext
public partial class LOCALDbContext : DbContext
{
    public LOCALDbContext()
    {
    }

    public LOCALDbContext(DbContextOptions<LOCALDbContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Dbtable1> Dbtable1s { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.HasAnnotation("Relational:Collation", "Latin1_General_CI_AS");

        modelBuilder.Entity<Dbtable1>(entity =>
        {
            entity.Property(e => e.Column1).ValueGeneratedNever();
        });

        OnModelCreatingPartial(modelBuilder);
    }

    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}


//API Project
//Controller
namespace API.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class TestController : ControllerBase
    {
        private readonly ITable1Repository _table1Repository;
        public TestController(ITable1Repository table1Repository)
        {
            _table1Repository = table1Repository;
        }

        // GET: <TestController>
        [HttpGet]
        public async Task<IEnumerable<Dbtable1>> Get()
        {
            return await _table1Repository.GetAsync();
        }
    }
}

//Program.cs
using DAL.Data;
using DAL.Repositories;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() { Title = "API", Version = "v1" });
});

var connectionString = builder.Configuration.GetConnectionString("DefaultConnectionString");
builder.Services.AddDbContext<LOCALDbContext>(options => options.UseSqlServer(connectionString));

builder.Services.AddScoped<ITable1Repository, Table1Repository>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"));
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();


//appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnectionString": "Server=localhost;Database=LOCALDatabase;User id=userid;password=password"
  }
}

//appsettings.Docker.json
{
  "ConnectionStrings": {
    "DefaultConnectionString": "Server=host.docker.internal;Database=LOCALDatabase;User id=userid;password=password"
  }
}

//DockerFile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src

COPY *.sln ./
COPY ./src/API/API.csproj ./src/API/
COPY ./src/DAL/DAL.csproj ./src/DAL/

RUN dotnet restore
COPY . .

RUN dotnet publish -c Release -o out

FROM base
WORKDIR /app

COPY --from=build /src/out .
ENTRYPOINT ["dotnet", "API.dll"]

docker build and run commands docker build -t test-api:v1 . docker run -it -p 7277:80 -e "ASPNETCORE_ENVIRONMENT=Docker" --name testapi test-api:v1

I have tested the containerized application with some local data (Non Db) and it works fine but SQL connection is causing the issue.

Here's my solution folder structure enter image description here

Following is the DB table script

USE [LOCALDatabase]
GO
/****** Object:  Table [dbo].[DBTable1]    Script Date: 04/11/2021 13:50:33 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DBTable1](
    [Column1] [int] NOT NULL,
    [Column2] [nvarchar](50) NULL,
    [Column3] [nvarchar](50) NULL,
 CONSTRAINT [PK_DBTable1] PRIMARY KEY CLUSTERED 
(
    [Column1] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT [dbo].[DBTable1] ([Column1], [Column2], [Column3]) VALUES (1, N'Richard', N'King')
INSERT [dbo].[DBTable1] ([Column1], [Column2], [Column3]) VALUES (2, N'Stuart', N'Philip')
Learning Curve
  • 1,449
  • 7
  • 30
  • 60
  • can you provide how do you run the docker container? either the docker-compose file or the docker cli command that starts the container – milo Nov 03 '21 at 15:16
  • 1
    I'm using docker cli command. I've updated my question with the build and run commands. – Learning Curve Nov 03 '21 at 15:23

2 Answers2

0

The problem is that when you specify localhost in the connection string, it resolves to the container not the outside host machine, in order to fix that use host.docker.internal instead of localhost and 127.0.0.1

Also in order to keep your existing method of running your API from outside docker operational, you could create a new app settings file :

appSettings.Docker.json

"ConnectionStrings": {
    "DefaultConnectionString": "Server=host.docker.internal;Database=LOCALDatabase;User id=userId;password=password"
  }

and change your docker cli to :

docker build -t test-api:v1 .

docker run -it -p 7277:80 -e "ASPNETCORE_ENVIRONMENT=Docker" --name testapi test-api:v1

sources:

milo
  • 445
  • 5
  • 12
  • Still getting the same error @milo "EventId":20004,"LogLevel":"Error","Category":"Microsoft.EntityFrameworkCore.Database.Connection","Message":"An error occurred using the connection to database \u0027LOCALDatabase\u0027 on server \u0027host.docker.internal\u0027.","State":{"Message":"An error occurred u sing the connection to database \u0027LOCALDatabase\u0027 on server \u0027host.docker.internal\u0027.","database":"LOCALDatabase","server":"host.docker.internal","{OriginalFormat}":"An error occurred using the connection to database \u0027{database}\u0027 on server \u0027{server}\u0027." – Learning Curve Nov 03 '21 at 16:23
  • I have added the appsettings.Docker.json file with suggested 'Server=host.docker.internal' and included environment tag in my docker run command as advised but don't see any difference in the error? – Learning Curve Nov 03 '21 at 16:30
  • I tried it and it works for me, can you provide a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) ? – milo Nov 04 '21 at 08:17
  • I've updated my original question with all the code and Docker file @milo. – Learning Curve Nov 04 '21 at 14:55
0

I have got it working by providing Host's IP address (ipv4 address) and Port Number in the connection string. The solution is explained in this thread. I have added the screenshots for little more clarity.

"DefaultConnectionString": "Server=192.168.*.*,1433;Database=LOCALDatabase;User id=mydbuserid;password=mydbpassword"

Steps followed to get it working with the IP and Port number

  1. From Window + R, Open compmgmt.msc
  2. Check if TCP/IP for Protocols for MSSQLSERVER is Enabled. enter image description here
  3. 1433 is the default port used by the SQL Server. Double click TCP/IP and go to the IP Addresses tab and check if 1433 port is there for TCP Port under IPAAll section. enter image description here
  4. Restart SQL Server service from Services section enter image description here
  5. Allow port 1433 to be accessible through Windows Firewall by following these instructions
Learning Curve
  • 1,449
  • 7
  • 30
  • 60