No parameterless constructor found after migrating to 2.0.0-preview

asp.net-core asp.net-core-2.0 c# entity-framework-core

Question

After migrating my project from EF Core/Asp.NET Core 1.1.2 to 2.0.0-preview2-latest in order to make use of the custom database function support, the migration engine will no longer work. I already updated the CLI tools to 2.0.0-preview2-final but it didn't fix the issue.

Here's all my Startup.cs and Context code for reference:

Startup.cs:

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();

        //if (env.IsDevelopment()) builder.AddUserSecrets<Startup>();

        builder.AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddLocalization(options => options.ResourcesPath = "Resources");
        services.AddOptions();
        services.Configure<Config>(Configuration.GetSection("AppSettings"));
        services.AddSingleton<IConfiguration>(Configuration);
        services.AddDbContext<MemoryContext>(
            options => options.UseSqlServer(Configuration.GetConnectionString("Database"), b => b.MigrationsAssembly("MemoryServer")));

        services.AddIdentity<User, IdentityRole<Guid>>()
            .AddEntityFrameworkStores<MemoryContext>()
            .AddDefaultTokenProviders();
        // Add framework services.
        services.AddMvc();

        services.ConfigureApplicationCookie(options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromDays(150);
            options.LoginPath = "/api/auth/login";
            options.LogoutPath = "/api/auth/logout";
        });
        services.Configure<IdentityOptions>(options =>
        {
            options.Password.RequireDigit = true;
            options.Password.RequiredLength = 8;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = true;
            options.Password.RequireLowercase = true;

            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
            options.Lockout.MaxFailedAccessAttempts = 10;

            options.User.RequireUniqueEmail = true;
            options.SignIn.RequireConfirmedEmail = false;
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        var supportedCultures = new[]
        {
            new CultureInfo("en-US"),
        };

        app.UseRequestLocalization(new RequestLocalizationOptions
        {
            DefaultRequestCulture = new RequestCulture("en-US"),
            SupportedCultures = supportedCultures,
            SupportedUICultures = supportedCultures
        });

        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
        else
            app.UseExceptionHandler("/Home/Error");

        app.UseAuthentication();

        app.UseMvcWithDefaultRoute();
    }
}

MemoryContext.cs:

public class MemoryContext : IdentityDbContext<User, IdentityRole<Guid>, Guid>
{
    public DbSet<Lesson> Lessons { get; set; }
    public DbSet<LessonAssignment> Assignments { get; set; }
    public DbSet<Review> Reviews { get; set; }
    public DbSet<UserList> UserLists { get; set; }
    public DbSet<Language> Languages { get; set; }

    public MemoryContext(DbContextOptions options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.Entity<UserListEntry>().HasKey(a => new {a.OwnerId, a.LessonId});
        builder.HasDbFunction(typeof(MemoryContext).GetMethod(nameof(Levenshtein)), funBuilder => {});
    }

    protected override void OnConfiguring(DbContextOptionsBuilder builder)
    {
        base.OnConfiguring(builder);
        builder.EnableSensitiveDataLogging();
    }

    public static int Levenshtein(string s1, string s2, int max) { throw new NotImplementedException(); }
}

Console output:

No parameterless constructor was found on 'MemoryContext'. Either add a parameterless constructor to 'MemoryContext' or add an implementation of 'IDesignTimeDbContextFactory' in the same assembly as 'MemoryContext'.

1
1
7/30/2017 6:23:08 AM

Accepted Answer

After being pointed to the note by Smit, here is the proper solution to the issue:

Due to a change in Asp.NET Core 2, the migrations tool can no longer simply use the Startup class as is. Instead, modify your Program.cs to something like this:

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .UseApplicationInsights()
            .Build();

        host.Run();
    }

    public static IWebHost BuildWebHost(string[] args) => 
        new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();
}

After doing this, the migrations tool should be able to instantiate your context again, and you won't have to hardcode a design time factory.

2
8/1/2017 11:10:13 AM

Popular Answer

For EF Core 2.0 RTM, Add this class to the same project where your context exists:

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
    public MyDbContext CreateDbContext(string[] args)
    {
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();
        var builder = new DbContextOptionsBuilder<MyDbContext>();
        var connectionString = configuration.GetConnectionString("DefaultConnection");
        builder.UseSqlServer(connectionString);
        return new MyDbContext(builder.Options);
    }
}


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