Code First Entity Framework with ASP.NET Core:初始迁移似乎期望表已经存在

Code First Entity Framework with ASP.NET Core: Initial migration seems to expect tables already exist

我正在尝试在 Visual Studio Code 中创建一个 Code First Entity Framework ASP.NET Core 2 项目。我一直在关注 Create a Web API with ASP.NET Core MVC and Visual Studio Code on Linux, macOS, and Windows 教程,该教程使用内存中的数据存储作为其 DbContext。我正在尝试将其移至 LocalDB。

教程 Getting Started with EF Core on ASP.NET Core with a New database 建议我应该能够通过迁移来完成此操作。

Once you have a model, you can use migrations to create a database.

Open the PMC:

Tools –> NuGet Package Manager –> Package Manager Console

Run Add-Migration InitialCreate to scaffold a migration to create the initial set of tables for your model. If you receive an error stating The term 'add-migration' is not recognized as the name of a cmdlet, close and reopen Visual Studio.

Run Update-Database to apply the new migration to the database. This command creates the database before applying migrations.

相当于使用包管理器控制台的 VS Code seems to be

dotnet ef migrations add InitialCreate

added EF's Design namespace with...

dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet restore

并在我的 csproj 中引用:

<ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>

但是当我之后尝试 dotnet ef migrations add 命令时,它的作用就像我的 TodoItems 模型的 table 需要已经存在 在数据库中。据我了解,迁移将 创建 基于我的模型的 table。

c:\Projects\TodoApi>dotnet ef migrations add InitialCreate -v
Using project 'c:\Projects\TodoApi\TodoApi.csproj'.
Using startup project 'c:\Projects\TodoApi\TodoApi.csproj'.
Writing 'c:\Projects\TodoApi\obj\TodoApi.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\UserName\AppData\Local\Temp\tmp945E.tmp /verbosity:quiet /nologo c:\Projects\TodoApi\TodoApi.csproj
Writing 'c:\Projects\TodoApi\obj\TodoApi.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\UserName\AppData\Local\Temp\tmp96FF.tmp /verbosity:quiet /nologo c:\Projects\TodoApi\TodoApi.csproj
dotnet build c:\Projects\TodoApi\TodoApi.csproj /verbosity:quiet /nologo

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.29
dotnet exec --depsfile c:\Projects\TodoApi\bin\Debug\netcoreapp2.0\TodoApi.deps.json --additionalprobingpath C:\Users\UserName\.nuget\packages --additionalprobingpath "C:\Program Files (x86)\Microsoft SDKs\NuGetPackagesFallback" --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig c:\Projects\TodoApi\bin\Debug\netcoreapp2.0\TodoApi.runtimeconfig.json C:\Users\UserName\.nuget\packages\microsoft.entityframeworkcore.tools.dotnet.0.0\tools\netcoreapp2.0\ef.dll migrations add InitialCreate --assembly c:\Projects\TodoApi\bin\Debug\netcoreapp2.0\TodoApi.dll --startup-assembly c:\Projects\TodoApi\bin\Debug\netcoreapp2.0\TodoApi.dll --project-dir c:\Projects\TodoApi\ --verbose --root-namespace TodoApi
Using assembly 'TodoApi'.
Using startup assembly 'TodoApi'.
Using application base 'c:\Projects\TodoApi\bin\Debug\netcoreapp2.0'.
Using working directory 'c:\Projects\TodoApi'.
Using root namespace 'TodoApi'.
Using project directory 'c:\Projects\TodoApi\'.
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider...
Finding BuildWebHost method...
Using environment 'Development'.
Using application service provider from BuildWebHost method on 'Program'.
Found DbContext 'DatabaseContext'.
Finding DbContext classes in the project...
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
      Failed executing DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT CASE
          WHEN EXISTS (
              SELECT 1
              FROM [TodoItems] AS [t])
          THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
      END
System.Data.SqlClient.SqlException (0x80131904): Invalid object name 'TodoItems'.

需要做什么来确保 table 是根据我的模型创建的?

我能想到的唯一问题是我有内联连接字符串而不是 appsettings.json,但我不确定为什么这会很重要,除非正在寻找迁移默认为一个配置。

public void ConfigureServices(IServiceCollection services)
{
    // In memory: services.AddDbContext<DatabaseContext>(opt => opt.UseInMemoryDatabase("TodoList")); // <<< REMOVED
    string strCxn = "Server=(localdb)\mssqllocaldb;Database=Contacts;Trusted_Connection=True;MultipleActiveResultSets=true"; // <<< ADDED

    services.AddDbContext<DatabaseContext>(options => options.UseSqlServer(strCxn)); // <<< ADDED
    services.AddMvc();
}

顺便说一句,这些是我对 TodoApi 教程的代码所做的唯一更改,以从内存数据库移动到 LocalDB。


更新: 首先,我尝试更改连接字符串中的数据库名称..

string strCxn = "Server=(localdb)\mssqllocaldb;Database=Contacts2;Trusted_Connection=True;MultipleActiveResultSets=true";

(将 Contacts 更改为 Contacts2,以防万一它认为,因为它最初找到了联系人数据库,所以已经发生了迁移...)

这也没有用,尽管错误的变化表明连接字符串正在工作并正在被读取。

Cannot open database "Contacts2" requested by the login. The login failed.
Login failed for user 'COMPUTER-NAME\UserName'.

作为 DbContext 构造函数的一部分,我正在播种数据...

public class DatabaseContext : DbContext    {
    public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
    {
        this.MinimallySeedDatabase();
    }

    public void MinimallySeedDatabase()
    {
        State NY = this.States.Where(s => s.Abbr.Equals("NY")).FirstOrDefault();
        if (null == NY)
        {
            NY = new State {
                Name = "New York",
                Abbr = "NY"
            };
            this.States.Add(NY);
            this.SaveChanges();
        }

        // etc etc etc...
    }

    public DbSet<Contact> Contacts {get; set;}
    public DbSet<Address> Addresses {get; set;}
    public DbSet<AddyType> AddyTypes {get; set;}
    public DbSet<State> States {get; set;}
}

部分迁移执行涉及 DbContext 实例化,显然 迁移 DbSet 的 table 之前。由于在此示例中,构造函数需要访问状态,并且由于迁移尚未达到它创建要从中提取的状态 table 的程度,因此迁移失败了。

作为 ,如果我把种子去掉,我就没事了。如果你想播种,你需要把它移到构造函数以外的地方,即使构造函数中的播种在迁移之外工作正常 attempt/in 正常的 Kestrel 测试。

对我来说,我只是在 运行 迁移时注释掉了 this.MinimallySeedDatabase();