Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the connection string from the SqlServerDBContextOptionsExtensions in ASP.Net Core

I am building up an ASP.Net Core API and I have not been able to find a way to get the connection string from the DBContextOptions.

I have the DBContext in my startup.cs as shown below;

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddEntityFrameworkSqlServer()
        .AddDbContext<MainContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MainConnection")));

    services.AddMvc()
       .AddJsonOptions(opt =>
        {
            opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        });
}

And in my Context class, I have the following constructor;

public MainContext(DbContextOptions<MainContext> options) : base(options)
{

}

but it doesn't work unless I add an actual connection string in the DBContext class, OnConfiguring method, as shown below;

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    //TODO Move connection string to a secure location
    optionsBuilder.UseSqlServer(@"Server= .....");

}

I can see that the Startup.cs is getting the correct connection string from the appsettings.json file when I debug and examine the value in Configuration.GetConnectionString("MainConnection").

I would think that passing the options into the DbContext class via DI would have passed the connection string but the DbContect class doesn't work unless I have that optionBuilder.UseSqlServer() in the OnConfiguring method.

I found this SO post https://stackoverflow.com/questions/33532599/asp-net-5-multiple-dbcontext-problems, that talks about using the following code to extract the connection string from the options property

public ResourceDbContext(DbContextOptions options) : base(options)
{
    _connectionString = ((SqlServerOptionsExtension)options.Extensions.First()).ConnectionString;
}


protected override void OnConfiguring(DbContextOptionsBuilder options)
{
    options.UseSqlServer(_connectionString);
}  

But when I try to use it, I find that there is no longer a First() method in options.Extensions

So, my first question is...

Why doesn't the DBContext class work without having to add the connection string in the OnConfiguring method

My second question is ...

If the connection string is required in the OnCOnfiguring method, how can I get it from the DbContextOptions options object rather than having to explicitly provide it in the OnConfiguring method --> optionsBuilder.UseSqlServer(@"Server= .....");

like image 528
EiEiGuy Avatar asked Dec 15 '16 16:12

EiEiGuy


2 Answers

At least for EF Core 1.1, you need to use FindExtension<SqlServerOptionsExtension>()

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure.Internal;

namespace MyNamespace
{
    public class MyContext : DbContext
    {
        public MyContext(DbContextOptions<MyContext> options) : base(options)
        {
            var sqlServerOptionsExtension = 
                   options.FindExtension<SqlServerOptionsExtension>();
            if(sqlServerOptionsExtension != null)
            {
                string connectionString = sqlServerOptionsExtension.ConnectionString;
            }
        }
    }
}

The null check is there in case you're using opt.UseInMemoryDatabase() in your Startup.cs

like image 170
MikeT Avatar answered Sep 28 '22 15:09

MikeT


Inside of your appsettings.json you would create the following:

{
     "Database" : {
          "ConnectionString" : "..."
      }
}

Then inside your ConfigureServices you would do the following:

services.AddSingleton(_ => Configuration);

That will basically populate the IConfigurationRoot property. Which you can inject anywhere, and have access to the connection string by doing:

private readonly IConfigurationRoot configuration;
private IDbConnection dbConnection { get; }

public Example(IConfigurationRoot configuration)
{
     this.Configuration = configuration;
     dbConnection = new SqlConnection(this.configuration.GetConnectionString("..."));
}

I structured this a bit odd, you really would simply pass the ConnectionString to another class, or method to inject but this demonstrates for you. But I believe Entity Framework 7 has a factory to directly accept the connection string. Hopefully this helps you out.

In Entity Framework it should be like this inside your ConfigureServices:

services.AddSingleton<dbContext>(_ => new dbContext(Configuration.GetConnectionString("...")));
public class dbContext : DbContext
{
     public dbContext(string dbConnection) : base(dbConnection)
     {

     }
}

Some extra documentation.

like image 20
Greg Avatar answered Sep 28 '22 14:09

Greg