简体   繁体   中英

How do I get an IConfiguration instance in my DbContext?

I've seen a number of tutorials that explain .NET core + EF configuration, but none of them explain how to get a Configuration instance in the DbContext .

The reason I need this is to get the db connection string:

namespace Models
{
    public class BlogContext : DbContext
    {

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

        protected override void OnConfiguring(DbContextOptionsBuilder options)
            => options.UseSqlite(Configuration.GetConnectionString("BlogDb"));

    }
}

My connection string is in an appsettings.json file:

{
    "ConnectionStrings": {
        "BlogDB": "Data Source=blogging.db"
    }
}

Most tutorials show an instance of IConfiguration in the db context. I suspect that it happens through the constructor:

private readonly IConfiguration Configuration;
public BlogContext(IConfiguration config) {
    Configuration = config;
}

But, how does this actually get injected?

This is how I'm using the context:

public class Seed
    {
        public static void SeedDatabase()
        {
            using var db = new BlogContext();
            ... stuff ...
        }
    }

How do I actually get the connection string from the settings file? It seems very confusing and a lot of work to do something very simple...

UPDATE

I'm using Seed like so:

public class Program
    {
        public static void Main(string[] args)
        {
            Seed.SeedDatabase();
        }
    }

How do I actually get an instance of DbContext into SeedDatabase ?

Based on the code above, it looks like there might be some mixing and matching on dependency injected and static code that's giving you a headache. Your middle example expects the configuration to come in through constructor injection, but then you're newing up an instance in your static method that can't use it.

Ideally, during your service registration, you'd do something like:

services.AddDbContext<BlogContext>(options => 
    options.UseSqlite(Configuration.GetConnectionString("BlogDb")));

This is registering the DB context into the service container with the options delegate set up to use the connection string from the appsettings, so your context would just look like:

public BlogContext(DbContextOptions<BlogContext> options)
   : base(options)
{
// ... 
}

And your context is no longer directly taking the dependency on the IConfiguration object. There's an example of that in Microsoft's docs here:

https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext#using-dbcontext-with-dependency-injection

With either route, though, you'd need to have the DbContext injected by the DI container in order to resolve the dependency tree, so trying to new up a context in the static method isn't working -- you have to get that dependency there somehow (hence your question). There's a bit of an XY problem here if I'm reading it right, so I'll try to cover both.

If you really need to maintain the code as written, you could inject the IConfiguration into the Seed class (or whatever other class you're calling from) and then pass it down to the static method via method injection. The Seed class itself (or whatever other instantiated class) would have to be registered to the DI container in order to get the IConfiguration passed into its own constructor and then injected where you're using it:

public class Seed
{
    private readonly IConfiguration _configuration;

    // The Seed instance itself could have the IConfiguration injected and be available to pass to the static method...
    public Seed(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    // ... but either way, when you call this, you have to pass in the dependency yourself, either from this class's instance, or from another one
    public static void SeedDatabase(IConfiguration configuration)
    {
        using (var db = new BlogContext(configuration))
        {
            //...
        }
    } 

}

More realistically, it looks like you're trying to seed the database (duh). If that's the case, take a look at EF Core's mechanisms for seeding:https://learn.microsoft.com/en-us/ef/core/modeling/data-seeding

UPDATE Adding some further information based on the additional code you posted. In your example, you're trying to hit your static seed function right at the program's entry point. Still hard to tell from the code whether you're building a host and service container, so I'll assume not for now. You can try something like the following:

using System;
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

public class Program
{
    public static void Main(string[] args)
    {
        var configuration = new ConfigurationBuilder()
            .AddJsonFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json")) // Grabs the appsettings JSON as the configuration source
            .Build();

        var dbOptions = new DbContextOptionsBuilder<BlogContext>()
            .UseSqlite(configuration.GetConnectionString("BlogDb"));

        Seed.SeedDatabase(new BlogContext(dbOptions)); // Pass in your prepared DbContext via method injection
        //...
    }
}

The EF Core seed capabilities will still probably help get you something more robust.

You should be using the DbContext with injection too. Note that anything to be injected needs to be added as a service. You can generally add a class as a service from the Startup class. In the case of a DbContext, you would use the extension method AddDbContext inside the ConfigureServices method.

services.AddDbContext<BlogContext>();

Once you use this code above, you will be able to inject IConfiguration the way you tried.

However this is not the best practice when setting up a web app. The IConfiguration is generally just used in the startup class. In this case, you can pass a parameter into the AddDbContext function to add a configuration.

services.AddDbContext<BloggingContext>(options => options.UseSqlite(Configuration.GetConnectionString("BlogDb"));

If you use this method, you should also remember to use the following constructor:

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

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

For the full documentation on this process, check this link: https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext .

Data seeding is also build into EF Core. You can check this:https://learn.microsoft.com/en-us/ef/core/modeling/data-seeding

Somewhere in your Seed class:

var config = new ConfigurationBuilder()
                 .SetBasePath(Directory.GetCurrentDirectory())
                 .AddEnvironmentVariables()
                 .Build();
var dbContext = new DbContext(config);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM