Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Net Core Application in Docker "LocalDB is not supported" without using a Local DB

I have a Net.Core Application running with a SQL-Database. The Connection string is in the environment variables.

On Local IIS the Application works fine.

Same Application as Docker-Container got following Error

fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HLPO85V83VNO", Request id "0HLPO85V83VNO:00000001": An unhandled exception was thrown by the application. System.PlatformNotSupportedException: LocalDB is not supported on this platform.
at System.Data.SqlClient.SNI.LocalDB.GetLocalDBConnectionString(String localDbInstance)
at System.Data.SqlClient.SNI.SNIProxy.GetLocalDBDataSource(String fullServerName, Boolean& error)

environment Variables for Docker: "DB_CONNECTION": "Server=hc-XXX; Database=IB33DB-Core_XXX; User Id=sa;Password=XXX"

Here is the setup in the Network

Network info

Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ib.api_core.Data;
using Microsoft.EntityFrameworkCore;
using ib.api_core.Models;

namespace ib.api_core
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

            string dbConnection = Environment.GetEnvironmentVariable("DB_CONNECTION");
            Console.WriteLine("Env var DB Connection: "+dbConnection);

            //Verweis auf den Datenbank Kontext
            services.AddDbContext<ibContext>(options =>
                options.UseSqlServer(dbConnection));

            //Verweis auf den Datenbank Kontext
//            services.AddDbContext<ibContext>(options =>
//                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }


            //1. Alle Anfragen in der Konsole loggen
            app.UseMiddleware<RequestResponseLoggingMiddleware>();
            //2. Prüfe Login und Berechtigung der Anfrage
            app.UseMiddleware<AuthenticationMiddleware>();

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

ibContext.cs

using System;
using Microsoft.EntityFrameworkCore;
namespace ib.api_core.Models
{
    public partial class ibContext : DbContext
    {

        public ibContext()
        {
        }

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

        public static ibContext GetContext()
        {
            var optionsBuilder = new DbContextOptionsBuilder<ibContext>();
            optionsBuilder.UseSqlServer(Environment.GetEnvironmentVariable("DB_CONNECTION"));
            return new ibContext(optionsBuilder.Options);
        }

[...]  


        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer(Environment.GetEnvironmentVariable("DB_CONNECTION"));
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
....
like image 658
Janneck Lange Avatar asked Sep 13 '19 12:09

Janneck Lange


2 Answers

The error message means that at some point, your code tries to access the database using a connection string configured for LocalDB. I suspect that in your appsettings.json, there is a connection string that uses LocalDB that is used at some point.

From the code samples I cannot spot the reason for that. Maybe some code bypasses the environment variable and reads the connection string from a configuration file or the container runs an old version of the code.


However, in a project based upon this sample I was able to override the connection string in the container:

The following code uses .NET Core configuration to get the connection string. The advantage of this approach is that you can provide the connection string in several ways and also override it when running the container.

Context

The context that is used in the sample is a simple one:

public class BloggingContext : DbContext
{
    public BloggingContext(DbContextOptions<BloggingContext> options)
        : base(options)
    { }

    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

Setup

In startup.cs, the context is registered and the connection string is retrieved using .NET Core configuration:

var connection = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<BloggingContext>
    (options => options.UseSqlServer(connection));

Dev configuration

For development, a LocalDB is used that is configured in appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.AspNetCore.NewDb;Trusted_Connection=True;ConnectRetryCount=0"
  }
}

Overriding the connection string for the container

When running the container, an environment variable named ConnectionStrings:DefaultConnection overrides the connection string from the appsettings file.

For debugging, I inserted an environment variable in launchsettings.json:

...
"Docker": {
  "commandName": "Docker",
  "launchBrowser": true,
  "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
  "environmentVariables": {
    "ASPNETCORE_URLS": "https://+:443;http://+:80",
    "ASPNETCORE_HTTPS_PORT": "44330",
    "ConnectionStrings:DefaultConnection": "Data Source=MY_IP\\MY_INSTANCE;Initial Catalog=TestDb;User Id=MY_USER;Password=MY_PWD"
  },
  "httpPort": 10000,
  "useSSL": true,
  "sslPort": 44330
}
...

When running in IIS Express, the connection string from appsettings.json is used, when running in a container, the SQL Server instance on the host is accessed as configured in the environment variable.

like image 192
Markus Avatar answered Nov 10 '22 15:11

Markus


It's a too late. But maybe it will help someone who face this problem.

You can use host.docker.internal in ConnectionString which will resolve to your host IP. For example (1433 - port sqlServer uses) "Server=host.docker.internal,1433;Database=yourDatabaseName;Integrated Security=False;MultipleActiveResultSets=true;User=DbUser;Password=DbPassword;"

like image 33
Oleksandr Vashchuk Avatar answered Nov 10 '22 16:11

Oleksandr Vashchuk