EF Code-First 迁移未检测到更改

问题描述 投票:0回答:1

我有一个数据库,需要在两个现有表之间引入新的必需 FK 关系。由于它们都有现有数据(并且产品已经在生产中运行),因此我需要采取更多步骤来添加新列和 FK 关系。

第一次迁移向现有表添加了一个新的、可为空的

Guid
列,该列将成为 FK 关系中的主要列。然后,我使用自定义 SQL 脚本使用另一个表中的相应值更新了表中的记录。 然后,我更新了模型,使相同的
Guid
不可为空,并生成了新的迁移,这还向列添加了索引。

到这里一切都很好。

然后,我在两个模型的配置中配置了 FK 关系并生成了新的迁移。然而,这种迁移似乎拒绝接受模型和配置的更改。

以下是模型定义和相应的配置。
感兴趣的属性与

Registry
有关。
公司

public class Company
{
    /// <summary>
    /// Gets or sets the unique identifier of the <see cref="Company"/>.
    /// </summary>
    public Guid Id { get; set; } = Guid.NewGuid();

    /// <summary>
    /// Gets or sets the name of the <see cref="Company"/>.
    /// </summary>
    public string Name { get; set; } = string.Empty;

    /// <summary>
    /// Gets or sets the unique identifier of the <see cref="Group"/> to which the <see cref="Company"/> belongs.
    /// </summary>
    public Guid? GroupId { get; set; }

    /// <summary>
    /// Gets or sets the unique identifier of the <see cref="Registry"/> from which the <see cref="Company"/> entity was created.
    /// </summary>
    public Guid RegistryId { get; set; }

    /// <summary>
    /// Gets the date and time at which the <see cref="Company"/> was created.
    /// </summary>
    public DateTime CreatedAt { get; } = DateTime.UtcNow;

    /// <summary>
    /// Gets or sets the unique identifier of the user who created the <see cref="Company"/>.
    /// </summary>
    public Guid CreatedBy { get; set; } = Guid.Empty;

    /// <summary>
    /// Gets or sets the date and time at which the <see cref="Company"/> was last updated.
    /// </summary>
    public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;

    /// <summary>
    /// Gets or sets a concurrency token used to prevent conflicting edits in a <see cref="Company"/> entity.
    /// </summary>
    [SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global", Justification = "Used by Entity Framework.")]
    public byte[] RowVersion { get; set; } = null!;

    /// <summary>
    /// Gets or sets the <see cref="Group"/> to which the <see cref="Company"/> is linked through <see cref="GroupId"/>.
    /// </summary>
    public Group? Group { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="Registry"/> to which the <see cref="Company"/> is linked through <see cref="RegistryId"/>.
    /// </summary>
    public Registry Registry { get; set; } = null!;

    /// <summary>
    /// Gets or sets a collection of <see cref="SystemIdentifier"/> linked to this <see cref="Company"/>.
    /// </summary>
    public ICollection<SystemIdentifier> SystemIdentifiers { get; set; } = null!;

    /// <summary>
    /// Gets or sets a collection of <see cref="User"/> linked to this <see cref="Company"/>.
    /// </summary>
    public ICollection<User> Users { get; set; } = null!;

    /// <summary>
    /// Gets or sets a collection of <see cref="Permission"/> linked to this <see cref="Company"/>.
    /// </summary>
    public ICollection<Permission> Permissions { get; set; } = null!;
}

注册表

public class Registry
{
    /// <summary>
    /// Gets or sets the unique identifier of the <see cref="Registry"/>.
    /// </summary>
    public Guid Id { get; set; } = Guid.NewGuid();

    /// <summary>
    /// Gets or sets a token required to verify the <see cref="Registry"/>.
    /// </summary>
    public string? RegistrationToken { get; set; }

    /// <summary>
    /// Gets or sets the name of the company to which the registered client belongs.
    /// </summary>
    public string? CompanyName { get; set; }

    /// <summary>
    /// Gets or sets a JSON string containing a list of requested users.
    /// </summary>
    public string? RequestedUsers { get; set; }

    /// <summary>
    /// Gets or sets a value indicating whether the specified user in the registry are handled.
    /// </summary>
    public bool IsHandled { get; set; }

    /// <summary>
    /// Gets or sets a concurrency token used to prevent conflicting edits to a <see cref="Registry"/> entity.
    /// </summary>
    public byte[] RowVersion { get; set; } = null!;

    /// <summary>
    /// Gets or sets the date and time at which the <see cref="Registry"/> was created.
    /// </summary>
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;

    /// <summary>
    /// Gets or sets the date and time at which the <see cref="Registry"/> was last updated.
    /// </summary>
    public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;

    /// <summary>
    /// Gets or sets a collection of <see cref="RegistryAgreement"/> entities linked to the <see cref="Registry"/>.
    /// </summary>
    public ICollection<RegistryAgreement> Agreements { get; set; } = null!;

    /// <summary>
    /// Gets or sets a collection fo <see cref="Company"/> entities linked to the <see cref="Registry"/>.
    /// </summary>
    public ICollection<Company> Companies { get; set; } = null!;
}

公司配置

public class CompanyConfiguration : IEntityTypeConfiguration<Company>
{
    /// <inheritdoc />
    public void Configure(EntityTypeBuilder<Company> builder)
    {
        builder = builder ?? throw new ArgumentNullException(nameof(builder));

        builder.HasKey(c => c.Id);
        builder.Property(c => c.Id)
            .HasDefaultValueSql("NEWID()");

        builder.Property(c => c.Name)
            .IsRequired()
            .HasMaxLength(75);

        builder.Property(c => c.RegistryId)
            .IsRequired();

        builder.Property(c => c.CreatedAt)
            .HasDefaultValueSql("getdate()")
            .ValueGeneratedOnAdd()
            .IsRequired();

        builder.Property(c => c.CreatedBy)
            .IsRequired();

        builder.Property(c => c.UpdatedAt)
            .HasDefaultValueSql("getdate()")
            .ValueGeneratedOnAddOrUpdate()
            .IsRequired();

        builder.Property(c => c.RowVersion)
            .IsRowVersion();

        builder.HasOne(c => c.Group)
            .WithMany(g => g.Companies)
            .HasForeignKey(c => c.GroupId)
            .OnDelete(DeleteBehavior.SetNull)
            .IsRequired(false);

        builder.HasOne(c => c.Registry)
            .WithMany(r => r.Companies)
            .HasForeignKey(c => c.RegistryId)
            .OnDelete(DeleteBehavior.Restrict)
            .IsRequired();

        builder.HasMany(c => c.Permissions)
            .WithOne(p => p.Company)
            .HasForeignKey(p => p.CompanyId)
            .OnDelete(DeleteBehavior.NoAction)
            .IsRequired(false);

        builder.HasMany(c => c.SystemIdentifiers)
            .WithOne(si => si.Company)
            .HasForeignKey(si => si.CompanyId)
            .OnDelete(DeleteBehavior.NoAction)
            .IsRequired(false);
    }
}

注册表

public class RegistryConfiguration : IEntityTypeConfiguration<Registry>
{
    /// <inheritdoc />
    public void Configure(EntityTypeBuilder<Registry> builder)
    {
        builder = builder ?? throw new ArgumentNullException(nameof(builder));

        builder.HasKey(r => r.Id);
        builder.Property(r => r.Id)
            .HasDefaultValueSql("NEWID()")
            .ValueGeneratedOnAdd()
            .IsRequired();

        builder.Property(r => r.IsHandled)
            .ValueGeneratedOnAdd()
            .HasDefaultValueSql("0")
            .HasColumnType("bit")
            .IsRequired();

        builder.Property(r => r.CreatedAt)
            .HasDefaultValueSql("getdate()")
            .ValueGeneratedOnAdd()
            .IsRequired();

        builder.Property(r => r.UpdatedAt)
            .HasDefaultValueSql("getdate()")
            .ValueGeneratedOnAddOrUpdate()
            .IsRequired();

        builder.Property(r => r.RowVersion)
            .IsRowVersion();

        builder.HasMany(r => r.Agreements)
            .WithOne(ra => ra.Registry)
            .HasForeignKey(ra => ra.RegistryId)
            .OnDelete(DeleteBehavior.NoAction);

        builder.HasMany(r => r.Companies)
            .WithOne(c => c.Registry)
            .HasForeignKey(c => c.RegistryId)
            .OnDelete(DeleteBehavior.NoAction);
    }
}

我在 StackOverflow 上针对其他类似问题尝试了一些发布的解决方案,但似乎都不起作用。这是我尝试过的:

  1. 只需添加另一个迁移即可。
    • 这是行不通的,因为迁移不会获取新的迁移。
  2. 在倒数第二个迁移上运行
    Update-Database
    ,然后删除并覆盖最后一个迁移。
    • 我认为这可能可行,因为它也必须接受之前迁移中所做的更改。然而这也不起作用,它甚至没有获取上次迁移中的更改。
  3. 在倒数第二个迁移上运行
    Update-Database
    ,删除最后一个迁移,删除快照中的更改,然后创建新迁移。
    • 这导致实体框架抛出错误,称它找不到导航属性,即使它在模型、配置和快照中明确定义,因为这不是我创建的任何迁移引入的更改为了这个新的 FK 关系。
我在这个问题上花了整整一个工作日,并决定在继续浪费另一个工作日之前发布这个问题。

我意识到这个问题已经发布了好几次,所以如果这里还有另一个问题有正确的答案,请随时将其标记为可能重复!

提前致谢!

c# .net-core entity-framework-core ef-code-first
1个回答
0
投票
可能您忘记将实体配置应用到 OnModelCreating 方法:

protected override void OnModelCreating(ModelBuilder modelBuilder) { new CompanyConfiguration().Configure(modelBuilder.Entity<Company>()); new RegistryConfiguration().Configure(modelBuilder.Entity<Registry>()); base.OnModelCreating(modelBuilder); }
    
© www.soinside.com 2019 - 2024. All rights reserved.