How to restart postgres sequence with Entity framework seeding migration?

.net-core .net-core-3.1 entity-framework-core postgresql seeding

Question

I have the following database seeder:

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 ...

As you can see I'm going to populate some table, that contains language's names (on Russian, for showing on UI), language's codes, some additional field - Sort (not important here) and primary key. Simple, right? Here is the table:

enter image description here

Then I create it inside my OnModelCreating:

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

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

Then I run $ dotnet ef database update and seeding works fine! But problems soon began.

When I try to insert new one language, .NET gives me:

Exception data: Severity: ERROR SqlState: 23505 MessageText: duplicate key value violates unique constraint "PK_Languages" Detail: Key ("LanguageId")=(1) already exists. SchemaName: public TableName: Languages ConstraintName: PK_Languages File: nbtinsert.c Line: 434

"Hmmmm lets try again" - I thought. And:

Exception data: Severity: ERROR SqlState: 23505 MessageText: duplicate key value violates unique constraint "PK_Languages" Detail: Key ("LanguageId")=(2) already exists. SchemaName: public TableName: Languages ConstraintName: PK_Languages File: nbtinsert.c Line: 434 Routine: _bt_check_unique

You see that? The same error but with another Primary key complaint! The first was: Key ("LanguageId")=(1) already exists. and the second Key ("LanguageId")=(2) already exists. !

So, what to do? I know this way:

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

But it's pretty uncomfortable to run this SQL in a console after seeding. Am I miss something? Maybe, there is a standard way for this, I mean using some EF API?

Update

I will show you my Language model:

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; }
    }
}

I make insert via my repository abstraction:

Base repository:

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();
        }
    }
}

And in the controller:

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");
}

Update 2

As asked in the comments I'll pin here all fluent api for Language model:

// 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();

Update 3

As asked @Roman Marusyk, I pine here SQL script for creating Languages table.

-- 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");

Hmm, now I see that don't have anything about auto increment. But my SQL client shows my:

enter image description here

1
1
4/6/2020 4:50:45 PM

Popular Answer

Add HasKey to model configuration

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

as @IvanStoev mentioned, by convention, the property LanguageId is already the primary key

Try to specify

  modelBuilder.Entity<Language>()
        .Property(p => p.LanguageId)
        .ValueGeneratedOnAdd();
1
4/2/2020 7:58:49 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow