DMur
DMur

Reputation: 661

ApplicationDBContext declares a parameterless constructor

Trying to learn EF Core, have just finished a course online and I am trying to get an integer added to a DB table. .Net 5.0.

I am getting an error when I build the application:

'AddDbContext' was called with configuration, but the context type 'ApplicationDbContext' only declares a parameterless constructor. This means that the configuration passed to 'AddDbContext' will never be used. If configuration is passed to 'AddDbContext', then 'ApplicationDbContext' should declare a constructor that accepts a DbContextOptions and must pass it to the base constructor for DbContext. Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.CheckContextConstructors()

I understand the problem is between ApplicationDBContext.cs and Startup.cs, one has parameters and one is parameterless, but I don't know how to fix it. Other answers online seem to be along the lines of "This is obvious, just fix it" which is not very helpful.

ApplicationDBContext

If I un-comment the empty ApplicationDbContext functions I no longer get the error, but when I run the program nothing is added to my DB.

using System;
using System.Collections.Generic;
using System.Text;
using MyApp.Areas.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace MyApp.Data
{
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public DbSet<RGWAction> RGWActions { get; set; }

        DBConnectionStringFactory GetDBConnectionString = new DBConnectionStringFactory();
        
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            string ConnectionString = GetDBConnectionString.DBConnectionString();

            optionsBuilder.UseSqlServer(ConnectionString).LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Command.Name },LogLevel.Information)
                .EnableSensitiveDataLogging(); //add to be able to see parameters in your log
        }
        /*
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        {

        }

        public ApplicationDbContext()
        {

        }
        */
    }
}

Startup.cs

public void ConfigureServices(IServiceCollection services) method

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

RGWAction.cs

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace MyApp.Areas.Identity
{
    public class RGWAction
    {
        public int Id { get; set; }
        public string Email { get; set; }
        public int Total { get; set; }
        public int ReduceMeat { get; set; }
    }
}

Program.cs

Just trying to add an integer to the DB on startup right now, to get it working as a learning exercise. My next step will be to trigger it from a UI button.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MyApp.Data;
using MyApp.Areas.Identity;

namespace MyApp
{
    public class Program
    {
        private static ApplicationDbContext _context = new ApplicationDbContext();

        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
            InsertNewInteger();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

        private static void InsertNewInteger()
        {
            _context.RGWActions.Add(new RGWAction { ReduceMeat = 1 });
            _context.SaveChanges();
        }
    }
}

DB Table: enter image description here

Advice would be much appreciated.

Upvotes: 0

Views: 1961

Answers (3)

Jan Jurn&#237;ček
Jan Jurn&#237;ček

Reputation: 136

Your controller:

public class YourController : Controller
{
    [HttpPost] // because data is updated
    public async Task<ActionResult> NewIntAction(
        [FromServices]
        INewIntService newIntService)
    {
            await newIntService.InsertNewInteger();
            ....
            return View(); // You will need a view NewIntAction in the folder Views/Your
    }
}

INewIntService interface :

public interface INewIntService
{
    Task InsertNewInteger();
}

NewIntService class:

public sealed class NewIntService : INewIntService
{
    public NewIntService(ApplicationDbContext context)
    {
        _context = context;
    }
    ApplicationDbContext _context;

    public async Task InsertNewInteger()
    {
        _context.RGWActions.Add(new RGWAction { ReduceMeat = 1 });
        await _context.SaveChangesAsync();
    }
}

Startup.cs:

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

Main program:

namespace MyApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
            InsertNewInteger();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

I need define no constructor of ApplicationDbContext because the instance of ApplicationDbContext is created by the infrastructure and injected as a dependency into the NewIntService constructor in its context parameter.

Upvotes: 1

Jan Jurn&#237;ček
Jan Jurn&#237;ček

Reputation: 136

You need an ApplicationDbContext constructor with a DbContextOptions parameter to insert option dependencies.

You need a default constructor without parameters because you call it explicitly in the _context field initializer. Try to omit this initialization, it is unnecessary if DI works well.

Upvotes: 1

DMur
DMur

Reputation: 661

Ahhhh, finally! :)

ApplicationDBContext needs all three methods. Does anyone know why?

OnConfiguring(DbContextOptionsBuilder optionsBuilder)
ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
ApplicationDbContext()

Also Program.cs was the wrong place for my InsertNewInteger() method. It needed to be in the controller for the page load, in this case index.cs controller.

Upvotes: 0

Related Questions