1

我有一个使用 EFCore 和 Pomelo.EntityFrameworkCore 的 dotnet 核心项目。我刚刚从我认为的 EFCore 3.1.0 更新到 5.0.7(撰写本文时的最新版本),同时 Pomelo 更新到 5.0.0

在我的项目中,我使用 EFCore 调用存储过程并将它们映射到视图。我已将视图添加到上下文中,如下所示:

modelBuilder.Entity<NameOfView>().HasNoKey().ToView(null);

自从更新 EFCore 和 Pomelo 以来,我现在注意到的是,每当我尝试运行迁移时,它不再忽略这些视图。根据视图是否在数据库快照中,我会在迁移中不断获取CreateTable或脚本。DropTable在更新之前,这些视图被简单地忽略了。

我需要为上下文中的视图设置一些属性以确保它们被忽略吗?

4

2 回答 2

2

您正在经历以下 EF Core 5.0 重大更改ToView() 的处理方式不同

新行为

现在ToView(string)除了将实体类型映射到视图之外,还将该实体类型标记为未映射到表。这会导致升级到 EF Core 5 后的第一次迁移,以尝试删除此实体类型的默认表,因为它不再被忽略。

我可以理解更改 fluent API 行为的原因,但不能理解迁移行为(你好 EF Core,向后兼容性?)。以及建议的“缓解”

使用以下代码将映射表标记为从迁移中排除:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<User>().ToTable("UserView", t => t.ExcludeFromMigrations());
}

虽然它似乎修复了升级行为后的第一次迁移,但它需要指定假表名。不幸的是ToTable,具有能力的方法重载ExcludeFromMigrations不接受null表名。

我建议的解决方法,似乎对现有和新的“未映射”实体都有效,是替换

.ToView(null)

.HasAnnotation(RelationalAnnotationNames.IsTableExcludedFromMigrations, true)

注意:这是 EF Core 的一般问题,不是特定于数据库提供程序的问题,因此与 MySQL 或 Pomelo 无关。

于 2021-06-10T12:29:40.833 回答
0

这似乎不是 Pomelo 或 EF Core 的问题,因为以下确实按预期工作:

  1. 创建一个包含以下内容的项目:

项目.csproj

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net5.0</TargetFramework>
        <Nullable>disable</Nullable>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.7">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.7" />
        <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.0" />
    </ItemGroup>

</Project>

程序.cs

using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public class IceCream
    {
        public int IceCreamId { get; set; }
        public string Name { get; set; }
        public DateTime BestServedBefore { get; set; }
    }

    public class ExpiredIceCream
    {
        public int IceCreamId { get; set; }
        public string Name { get; set; }
        public DateTime BestServedBefore { get; set; }
        public int DaysExpired { get; set; }
    }

    public class Context : DbContext
    {
        public DbSet<IceCream> IceCreams { get; set; }
        public DbSet<ExpiredIceCream> ExpiredIceCreams { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                var connectionString = "server=127.0.0.1;port=3306;user=root;password=;database=So67919519_01";
                var serverVersion = ServerVersion.AutoDetect(connectionString);
                optionsBuilder.UseMySql(connectionString, serverVersion)
                    .UseLoggerFactory(
                        LoggerFactory.Create(
                            configure => configure
                                .AddConsole()
                                .AddFilter(level => level >= LogLevel.Information)))
                    .EnableSensitiveDataLogging()
                    .EnableDetailedErrors();
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IceCream>()
                .HasData(
                    new IceCream {IceCreamId = 1, Name = "Vanilla", BestServedBefore = new DateTime(2021, 1, 1)},
                    new IceCream {IceCreamId = 2, Name = "Chocolate", BestServedBefore = new DateTime(2021, 5, 15)},
                    new IceCream {IceCreamId = 3, Name = "Matcha", BestServedBefore = DateTime.Today.AddYears(5)});

            modelBuilder.Entity<ExpiredIceCream>(
                entity =>
                {
                    entity.ToView("ExpiredIceCreams");
                    entity.HasNoKey();
                });
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using var context = new Context();

            var expiredIceCreams = context.ExpiredIceCreams.ToList();
            
            Trace.Assert(expiredIceCreams.Count == 2);
        }
    }
}

创建初始迁移:

dotnet ef migrations add Initial

将以下代码添加到Up()生成Migrations/..._Initial.cs文件中方法的末尾:

migrationBuilder.Sql(@"
    CREATE VIEW `ExpiredIceCreams` AS
        SELECT *, DATEDIFF(CURDATE(), `BestServedBefore`) AS DaysExpired
        FROM `IceCreams`
        WHERE `BestServedBefore` <= CURDATE();");

将以下代码添加到Down()生成Migrations/..._Initial.cs文件中方法的开头:

migrationBuilder.Sql(@"DROP VIEW `ExpiredIceCreams`;");

更新您的数据库:

dotnet ef database update

运行项目以确保它按预期工作。

然后添加第二个迁移:

dotnet ef migrations add Second

看一下生成的Migrations/..._Second.cs文件。它不应包含有关您的ExpiredIceCreams视图的任何操作。


从这往哪儿走

我将从区分新旧...<MigrationName>.Design.cs文件开始,看看发生了什么。

随意发布您的迁移文件内容,以便我们查看。

(也许这与某些字符集或排序规则问题/更改有关。)

于 2021-06-10T11:48:24.863 回答