使用 DotNetCore 解决方案WebApi
项目和一个单独的Data
包含实体框架实现的项目。我们一直在升级库,因此我们使用所有最新的核心内容。
In the Data
项目,我们创建了一个ApplicationDbContextFactory
为了创建迁移(需要无参数构造函数)。由于添加迁移时无参数构造函数约束,无法注入IOptions<>
轻松访问appsettings.json
价值观。我们最终使用了ConfigurationBuilder
拉入WebApi
's appsettings.json
文件。
我们最近改变了ApplicationDbContextFactory
也拉进来user-secrets
。这允许每个开发人员使用自定义连接字符串,而不必忽略文件或记住不要提交某些内容。
自从进行此更改以来,使用dotnet ef migrations add MIGRATION_NAME
在命令行中工作得很好。然而,使用add-migration MIGRATION_NAME
Visual Studio 的包管理器控制台中的程序现在似乎已损坏,并出现以下错误:
add-migration :使用“1”个参数调用“Substring”时出现异常:
“StartIndex 不能小于零。参数名称:startIndex”处
行:1 字符:1 + 添加迁移测试 + ~~~~~~~~~~~~~~~~~~~~~~
+ 类别信息:未指定:(:) [添加迁移]、MethodInitationException
+FullyQualifiedErrorId:ArgumentOutOfRangeException,添加迁移
我尝试了该命令的一些变体,看看它是否需要指定上下文(除其他外),但似乎没有什么可以解决这个错误。它似乎永远不会通过构造函数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();
and opts.MigrationsAssembly("Data");
,但我不知道它们在这种情况下有什么区别。
我的问题:
- 这最初是在 Core 的 RC 时代实现的,可能有点过时了。在创建迁移时是否有更好的方法来完成提取用户机密值?这样用工厂还有事吗?
- 有人知道为什么我们会在 Visual Studio 的包管理器控制台中收到该错误吗?