首页 > 解决方案 > 如何使用实体框架播种迁移重新启动 postgres 序列?

问题描述

我有以下数据库播种机:

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

正如评论中所问的,我将在这里为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 客户端显示了我的:

在此处输入图像描述

标签: postgresql.net-coreentity-framework-coreseeding.net-core-3.1

解决方案


添加HasKey到模型配置

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

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

尝试指定

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

推荐阅读