【发布时间】:2017-05-05 13:53:22
【问题描述】:
使用 WebApi 项目和包含实体框架实现的单独 Data 项目开发 DotNetCore 解决方案。我们一直在升级库,因此我们正在使用所有最新的核心内容。
在Data 项目中,我们创建了ApplicationDbContextFactory 以创建迁移(需要无参数构造函数)。由于添加迁移时的无参数构造函数约束,您无法注入 IOptions<> 以轻松访问 appsettings.json 值。我们最终使用ConfigurationBuilder 拉入WebApi 的appsettings.json 文件。
我们最近将ApplicationDbContextFactory 更改为也加入user-secrets。这允许每个开发人员使用自定义连接字符串,而不必忽略文件或记住不要提交任何内容。
由于进行了此更改,因此在命令行中使用 dotnet ef migrations add MIGRATION_NAME 可以正常工作。但是,在 Visual Studio 的包管理器控制台中使用 add-migration MIGRATION_NAME 现在似乎被破坏并出现以下错误:
add-migration : 使用“1”参数调用“子字符串”的异常: “StartIndex 不能小于零。参数名称:startIndex” At line:1 char:1 + add-migration TESTING + ~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Add-Migration], MethodInvocationException + FullyQualifiedErrorId : ArgumentOutOfRangeException,Add-Migration
我尝试了该命令的一些变体,以查看它是否需要指定上下文(除其他外),但似乎没有任何方法可以解决此错误。它似乎永远无法通过 ApplicationDbContextFactory 中的构造函数。
这是我所指的代码:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Models.Domain.Settings;
using System;
using System.Diagnostics;
namespace Data
{
public class ApplicationDbContextFactory : IDbContextFactory<ApplicationDbContext>
{
private readonly SolutionSettings _settings;
// In order to use 'add-migration' in Visual Studio, you have to have a parameterless constructor.
// Otherwise you get "No parameterless constructor defined for this object." when creating a migration.
public ApplicationDbContextFactory()
{
}
public ApplicationDbContextFactory(IOptions<SolutionSettings> settings)
{
_settings = settings.Value;
}
public ApplicationDbContext Create(DbContextFactoryOptions options)
{
// If the IOptions signature was hit, we can just pull the dbconnection from settings
if (_settings != null && _settings.DbConnection != null)
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(_settings.DbConnection, opts => {
opts.EnableRetryOnFailure();
opts.MigrationsAssembly("Data");
});
return new ApplicationDbContext(optionsBuilder.Options);
}
else
{
// Otherwise, we have to get the settings manually...
return Create(options.ContentRootPath, options.EnvironmentName);
}
}
private ApplicationDbContext Create(string basePath, string environmentName)
{
// HACK: To pull from WebApi\appsettings.json
basePath = basePath.Replace("Data", "WebApi");
Console.Write($"PATH & ENV: {basePath}, {environmentName}" + Environment.NewLine);
// Pull in the WebApi\appsettings.json files, apply user secrets
var builder = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environmentName.ToLower()}.json", optional: true, reloadOnChange: true)
// This needs to match the UserSecretsId value in the WebApi.csproj
// Also added a UserSecretsId key with the same value to Data.csproj to suppress a warning
// Adding this was the only way it would actually override values with user-secret values
.AddUserSecrets("USER_SECRETS_ID")
.AddEnvironmentVariables();
var config = builder.Build();
var connectionString = config["SolutionSettings:DbConnection"];
Console.Write($"CONNECTION STRING: {connectionString}" + Environment.NewLine);
return Create(connectionString);
}
private ApplicationDbContext Create(string connectionString)
{
if (string.IsNullOrEmpty(connectionString))
throw new ArgumentException(
$"{nameof(connectionString)} is null or empty.",
nameof(connectionString));
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(connectionString, options => {
options.EnableRetryOnFailure();
options.MigrationsAssembly("Data");
});
return new ApplicationDbContext(optionsBuilder.Options);
}
}
}
作为旁注;在解决此问题时,我添加了 opts.EnableRetryOnFailure(); 和 opts.MigrationsAssembly("Data");,但我不知道它们在这种情况下有什么不同。
我的问题:
- 这最初是在 Core 的 RC 时代实现的,可能有点过时了。在创建迁移时,是否有更好的方法来完成提取用户机密值?像这样使用工厂还有什么意义吗?
- 有人知道为什么我们会在 Visual Studio 的包管理器控制台中收到该错误吗?
【问题讨论】:
标签: c# asp.net-core entity-framework-core