【问题标题】:How to restart postgres sequence with Entity framework seeding migration?如何使用实体框架播种迁移重新启动 postgres 序列?
【发布时间】:2020-07-14 15:34:42
【问题描述】:

我有以下数据库播种器:

public partial class Seed_Languages : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.InsertData(
                table: "Languages",
                columns: new[] { "LanguageId", "LangCode", "LangName", "Sort" },
                values: new object[,]
                {
                    { 1, "AU", "Австралия", 0 },
                    { 159, "CX", "Остров Рождества", 0 },
                    { 160, "PN", "Острова Питкэрн", 0 },
                    { 161, "SH", "Острова Святой Елены, Вознесения и Тристан-да-Кунья", 0 },
                    { 162, "PK", "Пакистан", 0 },
                    { 163, "PW", "Палау", 0 },

.... and so on ...

如您所见,我将填充一些表格,其中包含语言名称(俄语,用于在 UI 上显示)、语言代码、一些附加字段 - Sort(此处不重要)和主键。很简单,对吧? 这是表格:

然后我在我的OnModelCreating 中创建它:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder); 

   // many fluent api calls 
   LanguagesSeeder.SeedLanguages(modelBuilder);
}

然后我运行$ dotnet ef database update 并且播种工作正常!但问题很快就开始了。

当我尝试插入新的一种语言时,.NET 给了我:

异常数据: 严重性:错误 SqlState:23505 MessageText:重复键值违反唯一约束“PK_Languages” 详细信息:键 ("LanguageId")=(1) 已存在。 架构名称:公共 表名:语言 约束名称:PK_Languages 文件:nbtinsert.c 线路:434

“嗯,让我们再试一次” - 我想。并且:

异常数据: 严重性:错误 SqlState:23505 MessageText:重复键值违反唯一约束“PK_Languages” 详细信息:键 ("LanguageId")=(2) 已存在。 架构名称:公共 表名:语言 约束名称:PK_Languages 文件:nbtinsert.c 线路:434 例程:_bt_check_unique

你看到了吗?同样的错误,但有另一个主键投诉!第一个是:Key ("LanguageId")=(1) already exists.,第二个是Key ("LanguageId")=(2) already exists.

那么,该怎么办?我是这样认识的:

ALTER SEQUENCE <name of sequence> RESTART WITH <your number is here>;

但是在播种后在控制台中运行这个 SQL 是很不舒服的。我错过了什么吗?也许,有一个标准的方法,我的意思是使用一些 EF API?

更新

我会告诉你我的Language模型:

namespace Domains
{
    public class Language
    {
        public int LanguageId { get; set; }

        public int Sort { get; set; }

        public List<Customer> Customers { get; set; }        

        public List<PushMessageLang> PushMessageLangs { get; set; }

        [NotMapped]
        public IEnumerable<PushMessage> PushMessages
        {
            get => PushMessageLangs?.Select(r => r.PushMessage);
            set => PushMessageLangs = value.Select(v => new PushMessageLang()
            {
                PushMessageId = v.PushMessageId
            }).ToList();
        }

        public string LangName { get; set; }

        public string LangCode { get; set; }
    }
}

我通过我的存储库抽象进行插入:

基础仓库:

public class BaseRepository<T, C> : IRepository<T>
    where T : class
    where C : DbContext
{
    protected C DataContext;
    private readonly DbSet<T> _dbset;

    public BaseRepository(C context)
    {
        DataContext = context;
        _dbset = context.Set<T>();
    }

    public virtual IQueryable<T> All => _dbset;

    public virtual async Task SaveAsync(T entity)
    {
        await _dbset.AddAsync(entity);
        await DataContext.SaveChangesAsync();
    }

    public async Task SaveAsync(List<T> entity)
    {
        await _dbset.AddRangeAsync(entity);
        await DataContext.SaveChangesAsync();
    }

    public virtual async Task UpdateAsync(T entity)
    {
        _dbset.Attach(entity).State = EntityState.Modified;
        _dbset.Update(entity);
        await DataContext.SaveChangesAsync();
    }

    public virtual async Task DeleteAsync(int id)
    {
        var dbEntity =  await _dbset.FindAsync(id);

        if (dbEntity != null)
        {
            _dbset.Remove(dbEntity);
            await DataContext.SaveChangesAsync();
        }
    }
}

在控制器中:

public async Task<IActionResult> Create([FromForm] LanguageViewModel viewModel)
{
    if (!ModelState.IsValid)
    {
        return View(viewModel);
    }

    var newLanguage = new Language()
    {
        Sort = viewModel.Sort,
        LangCode = viewModel.Code,
        LangName = viewModel.Name
    };

    await _languageRepository.SaveAsync(newLanguage);

    return RedirectToAction("Index");
}

更新 2

根据 cmets 的要求,我将在这里为 Language 模型固定所有流利的 api:

// many to many with `Message` entity
modelBuilder.Entity<PushMessageLang>()
    .HasKey(bc => new { bc.PushLangId, bc.PushMessageId });

modelBuilder.Entity<PushMessageLang>()
    .HasOne(bc => bc.Language)
    .WithMany(b => b.PushMessageLangs)
    .HasForeignKey(bc => bc.PushLangId)
    .OnDelete(DeleteBehavior.Cascade);

modelBuilder.Entity<PushMessageLang>()
    .HasOne(bc => bc.PushMessage)
    .WithMany(c => c.PushMessageLangs)
    .HasForeignKey(bc => bc.PushMessageId)
    .OnDelete(DeleteBehavior.Cascade);

// has unique language code
modelBuilder.Entity<Language>()
    .HasIndex(x => x.LangCode).IsUnique();

更新 3

正如@Roman Marusyk 所问,我在这里使用SQL 脚本来创建Languages 表。

-- auto-generated definition
create table "Languages"
(
    "LanguageId" integer generated by default as identity
        constraint "PK_Languages"
            primary key,
    "LangName"   text,
    "LangCode"   text,
    "Sort"       integer default 0 not null
);

alter table "Languages"
    owner to makeapp_pushes;

create unique index "IX_Languages_LangCode"
    on "Languages" ("LangCode");

嗯,现在我看到没有关于自动增量的任何内容。 但是我的 SQL 客户端显示了我的:

【问题讨论】:

  • 向我们展示您的Language 模型以及如何制作插入物。 LanguageId 应该在数据库中有 SERIAL 类型以自动增加值。或者你应该在代码中手动完成
  • @RomanMarusyk 完成!你能查一下吗?
  • @RomanMarusyk DataGrip(我的数据库客户端)给我发短信说 LanguageId 是整数。但它也有autoincrement 标签。如果我尝试插入以类似方式(但没有播种)创建的表中,我可以使用自动增量插入。没有任何 PK 错误。
  • 你的模型配置在哪里?
  • 是的,Fluent api for Language

标签: postgresql .net-core entity-framework-core seeding .net-core-3.1


【解决方案1】:

在模型配置中添加HasKey

modelBuilder.Entity<Language>()
    .HasKey(x => x.LanguageId)
    .HasIndex(x => x.LangCode).IsUnique();

正如@IvanStoev 提到的,按照惯例,LanguageId 属性已经是主键

尝试指定

  modelBuilder.Entity<Language>()
        .Property(p => p.LanguageId)
        .ValueGeneratedOnAdd();

【讨论】:

  • 谢谢!我会尽快尝试!我将在这里报告结果。好吗?
  • 这不会有任何区别 - 在名为 Language 的类中名为 LanguageId 的属性已经是主键 by convention
  • @RomanMarusyk 我试过了。感谢您的帮助,但它不起作用。如果我添加 `modelBuilder.Entity() .Property(p => p.LanguageId) .ValueGeneratedOnAdd();` 并进行新的迁移 - 迁移为空。它对数据库没有影响。当我尝试插入新的Language 时,我遇到了同样的错误。
  • 尝试删除表 - 指定 ValueGeneratedOnAdd - 创建一个新的迁移并运行。见github.com/npgsql/efcore.pg/issues/73
  • @RomanMarusyk 没有。我试过了,同样的问题。我有相同的 SQL 没有序列或关于自动增量的东西:(
【解决方案2】:

在迁移中我手动添加了这个字符串:

migrationBuilder.RestartSequence("Languages_LanguageId_seq", 251, "public");

其中Languages_LanguageId_seq - 序列名称, 251 - 序列的开始数(PK 值), public - 方案名称。

这里是documentation。现在我可以毫无错误地插入了。

【讨论】:

    猜你喜欢
    • 2013-08-30
    • 1970-01-01
    • 2013-09-16
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    • 2023-04-09
    • 2020-04-13
    相关资源
    最近更新 更多