Как перезапустить postgres последовательность с миграцией заполнения среды Entity Framework? - PullRequest
1 голос
/ 02 апреля 2020

У меня есть следующая база данных:

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

Как вы видите, я собираюсь заполнить некоторую таблицу, которая содержит названия языков (на русском языке, для отображения в пользовательском интерфейсе), коды языка, некоторые дополнительное поле - Sort (здесь не важно) и первичный ключ. Просто, правда? Вот таблица:

enter image description here

Затем я создаю ее внутри моего 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 "Detail: Key (" LanguageId ") = (1) уже существует. SchemaName: public TableName: Languages ​​ConstraintName: PK_Languages ​​Файл: nbtinsert. c Строка: 434

«Хмммм, давайте попробуем снова», - подумал я. И:

Исключительные данные: Серьезность: ОШИБКА SqlState: 23505 MessageText: дубликат значения ключа нарушает уникальное ограничение "PK_Languages" Подробно: Ключ ("LanguageId") = (2) уже существует. SchemaName: public TableName: Languages ​​ConstraintName: 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

Как и просят в комментариях, я здесь прикреплю все беглые API для Language модели:

// 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 клиент показывает мой:

enter image description here

Ответы [ 2 ]

1 голос
/ 02 апреля 2020

Добавить HasKey к конфигурации модели

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

, как @IvanStoev упомянул, по соглашению, свойство LanguageId уже является первичным ключом

Попробуйте указать

  modelBuilder.Entity<Language>()
        .Property(p => p.LanguageId)
        .ValueGeneratedOnAdd();
0 голосов
/ 06 апреля 2020

В миграции я добавил вручную эту строку:

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

, где Languages_LanguageId_seq - имя последовательности, 251 - номер начала последовательности (значение PK), public - Название схемы

Вот документация . Теперь я могу вставить без каких-либо ошибок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...