What goes into DbContextOptions when invoking a new DbContext?

asp.net-core entity-framework entity-framework-core

Question

I am not using DI and simply want to invoke a DbContext from within my controller. I am struggling to figure out what the 'options' should be?

ApplicationDbContext.cs

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{

    public DbSet<Gig> Gigs { get; set; }
    public DbSet<Genre> Genres { get; set; }


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

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);
    }
}

GigsController.cs

    public class GigsController : Controller
{
    private ApplicationDbContext _context;

    public GigsController()
    {
        _context = new ApplicationDbContext();
    }


    public IActionResult Create()
    {
        var viewModel = new GigFormViewModel
        {
            Genres = _context.Genres.ToList()
        };


        return View(viewModel);
    }
}

The issue is stemming in my GigsController constructor:

_context = new ApplicationDbContext();

I am erroring out because I need to pass something into the ApplicationDbContext. There is no argument given that corresponds to the required formal parameter 'options' of 'ApplicationDbContext.ApplicationDbContext(DbContextOptions)'

I tried creating a default constructor in ApplicationDbContext derived from base(), but that didn't work either.

In my startup.cs, I have configured the ApplicationDbContext

        public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        services.AddMvc();

        // Add application services.
        services.AddTransient<IEmailSender, AuthMessageSender>();
        services.AddTransient<ISmsSender, AuthMessageSender>();
    }
1
41
7/17/2016 1:30:06 AM

Accepted Answer

If you really want to create the context manually, then you can configure it like this:

var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(Configuration.GetConnectionStringSecureValue("DefaultConnection"));
_context = new ApplicationDbContext(optionsBuilder.Options); 

(The DbContextOptionsBuilder<ApplicationDbContext> class is the type of options argument in services.AddDbContext<ApplicationDbContext>(options =>). But in the controller, you don't have access to Configuration object, so you would have to expose it as a static field in Startup.cs or use some other trick, which is all bad practice.

The best way to obtain ApplicationDbContext is to get it through DI:

public GigsController(ApplicationDbContext context)
{
    _context = context;
}

The DI container will take care of instantiating and disposing of ApplicationDbContext. Note that you have everything correctly configured in Startup.cs:

services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

That's configuring DI, so why not just use it?

One more note about the default constructor of DbContext: In EF6 it was done like this: public ApplicationDbContext(): base("DefaultConnection") {}. Then the base object would use System.Configuration.ConfigurationManager static class to obtain the connection string named DefaultConnection from web.config. The new Asp.net Core and EF Core is designed to be as much decoupled as possible, so it should not take dependencies on any configuration system. Instead, you just pass a DbContextOptions object - creating that object and configuring it is a separate concern.

61
11/1/2018 4:38:06 AM

Popular Answer

This is how I would do it:

public class GigsController : Controller
{
    private readonly IConfiguration _configuration;
    private string _connectionString;
    DbContextOptionsBuilder<ApplicationDbContext> _optionsBuilder;

    public GigsController (IConfiguration configuration)
    {
        _configuration = configuration;
        _optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
        _connectionString = _configuration.GetConnectionString("DefaultConnection");
        _optionsBuilder.UseSqlServer(_connectionString);
    }

    public IActionResult Index()
    {
        using(ApplicationDbContext _context = new ApplicationDbContext(_optionsBuilder.Options))
        {
             // .....Do something here
        }
    }
}

Recently I was migrating a very large dataset into database(around 10 million) and one context instance will quick eat up all my memory. Thus I had to create a new Context instance and dispose the old one after certain threshold to release memory.

This is not an elegant solution but worked for me.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow