迁移到 2.0.0-preview 后未找到无参数构造函数
No parameterless constructor found after migrating to 2.0.0-preview
将我的项目从 EF Core/Asp.NET Core 1.1.2 迁移到 2.0.0-preview2-latest 以使用自定义数据库功能支持后,迁移引擎将不再工作。我已经将 CLI 工具更新到 2.0.0-preview2-final 但它没有解决问题。
这是我所有的Startup.cs和上下文代码供参考:
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(); }
}
控制台输出:
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'.
经 Smit 指点后,问题的正确解决方案如下:
由于 Asp.NET Core 2 的变化,迁移工具不能再简单地使用 Startup class。相反,将您的 Program.cs 修改为如下内容:
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();
}
执行此操作后,迁移工具应该能够再次实例化您的上下文,并且您不必对设计时工厂进行硬编码。
对于 EF Core 2.0 RTM,将此 class 添加到您的上下文所在的同一项目中:
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);
}
}
将我的项目从 EF Core/Asp.NET Core 1.1.2 迁移到 2.0.0-preview2-latest 以使用自定义数据库功能支持后,迁移引擎将不再工作。我已经将 CLI 工具更新到 2.0.0-preview2-final 但它没有解决问题。
这是我所有的Startup.cs和上下文代码供参考:
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(); }
}
控制台输出:
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'.
经 Smit 指点后,问题的正确解决方案如下:
由于 Asp.NET Core 2 的变化,迁移工具不能再简单地使用 Startup class。相反,将您的 Program.cs 修改为如下内容:
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();
}
执行此操作后,迁移工具应该能够再次实例化您的上下文,并且您不必对设计时工厂进行硬编码。
对于 EF Core 2.0 RTM,将此 class 添加到您的上下文所在的同一项目中:
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);
}
}