Lavorare su una soluzione DotNetCore con un progetto WebApi
e un progetto Data
separato che ospita l'implementazione di Entity Framework. Stiamo aggiornando le librerie man mano che escono, quindi stiamo utilizzando tutte le novità più recenti.
Nel progetto Data
, abbiamo creato un'applicazione ApplicationDbContextFactory
per creare migrazioni (è necessario un costruttore senza parametri). A causa del vincolo del costruttore parametrico quando si aggiunge una migrazione, non è possibile iniettare IOptions<>
per accedere facilmente ai valori di appsettings.json
. Abbiamo finito per utilizzare un ConfigurationBuilder
per WebApi
i file appsettings.json
.
Recentemente abbiamo modificato ApplicationDbContextFactory
per inserire anche user-secrets
. Ciò consente a ogni sviluppatore di utilizzare una stringa di connessione personalizzata senza dover ignorare un file o ricordare di non commettere qualcosa.
Dopo aver apportato questa modifica, l'utilizzo delle dotnet ef migrations add MIGRATION_NAME
funziona perfettamente nella riga di comando. Tuttavia, l'utilizzo di add-migration MIGRATION_NAME
nella console di Gestione pacchetti di Visual Studio ora sembra interrotto con il seguente errore:
add-migration: chiamata di eccezione "Substring" con argomento (i) "1": "StartIndex non può essere inferiore a 0. Nome parametro: startIndex" Alla riga: 1 carattere: 1 + add-migration TESTING + ~~~ ~~~~~~~~~~~~~~~~~~~ + CategoryInfo: NotSpecified: (:) [Add-Migration], MethodInvocationException + FullyQualifiedErrorId: ArgumentOutOfRangeException, Add-Migration
Ho provato alcune varianti del comando per vedere se fosse necessario specificare il contesto (tra le altre cose), ma nulla sembrava aggirare questo errore. Non sembra mai superare il costruttore in ApplicationDbContextFactory
.
Ecco il codice a cui mi riferisco:
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);
}
}
}
Come nota a margine; durante la risoluzione dei problemi ho aggiunto opts.EnableRetryOnFailure();
e opts.MigrationsAssembly("Data");
, ma non so che facciano alcuna differenza in questo contesto.
Le mie domande:
È passato molto tempo da quando lo hai pubblicato, ma ho appena avuto questo errore e ho scoperto il motivo (anche se non ha senso)
Il problema è nella linea
console.Write($"CONNECTION STRING: {connectionString}" + Environment.NewLine);
Se romove i due punti dopo CONNECTION STRING funziona. Non ho idea del perché i due punti nell'interpolazione stiano causando questo errore