Изменение имени по умолчанию для столбцов объединения, созданных EF Core

Когда этот код запускается (к вашему сведению, общий <TDbEntity> — это VgnItmDbEntity):

var entity = await context.Set<TDbEntity>().Include(e => e.VgnItmImages)
    .ThenInclude(vi => vi.Image)
    .FirstOrDefaultAsync(v => v.Id == id);

Я получаю ошибку:

Произошло исключение: CLR/Npgsql.PostgresException Выброшено исключение: «Npgsql.PostgresException» в System.Private.CoreLib.dll: «42703: столбец v0.VgnItmDbEntityId не существует

ПОЛОЖЕНИЕ: 1163 фута Npgsql.Internal.NpgsqlConnector.d__233.MoveNext()
в System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder1.StateMachineBox1.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 токен) в Npgsql.NpgsqlDataReader.d__52.MoveNext()
в Npgsql.NpgsqlDataReader.d__52.MoveNext() в Npgsql.NpgsqlCommand.d__119.MoveNext() в Npgsql.NpgsqlCommand.d__119.MoveNext() в Npgsql.NpgsqlCommand.d__112.MoveNext() в Microsoft.EntityFrameworkCore.Storage.RelationalCommand.d__18.MoveNext() в Microsoft.EntityFrameworkCore.Storage.RelationalCommand.d__18.MoveNext() в Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.<InitializeReaderAsync>d__21.MoveNext() at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.<ExecuteAsync>d__72.MoveNext() в Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.<MoveNextAsync>d__20.MoveNext() at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable1.ConfiguredValueTaskAwaiter.GetResult() в Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.d__151.MoveNext() at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleOrDefaultAsync>d__151.MoveNext() в Vepo.Data.VgnItmsRepository`5.d__9.MoveNext() в /Users/benjaminfarquhar/dev/vepo_back_end/Vepo.Data/Repositories/VgnItms/VgnItmsRepository.cs:line 144

Это мои сущности и их конфигурация в OnModelCreating:

public class VgnItmDbEntity : CreatedBySomeoneDbEntity
{
    [Required]
    public string Name { get; set; }

    [Required]
    public string CompanyName { get; set; }

    [MaxLength(3000)]
    public string Description { get; set; }

    [MaxLength(900)]
    public string WebPage { get; set; }

    public string Instagram { get; set; }
    public string Facebook { get; set; }

    [Required]
    public int IsNotVeganCount { get; set; } = 0;

    [Required]
    public int IsVeganCount { get; set; } = 0;

    [Required]
    public int RatingsCount { get; set; } = 0;

    public double? Rating { get; set; }

    [Required]
    public List<int> Tags { get; set; }

    public List<int> SecondaryTags { get; set; }

    public int? PricePoint { get; set; }

    public ICollection<VgnItmImageDbEntity> VgnItmImages { get; set; }

    public string Discriminator { get; set; }
}

public class VgnItmImageDbEntity : CreatedBySomeoneDbEntity
{
    [ForeignKey(nameof(VgnItm))]
    public int VgnItmId { get; set; }
    public VgnItmDbEntity VgnItm { get; set; }

    [ForeignKey(nameof(Image))]
    public int ImageId { get; set; }
    public ImageDbEntity Image { get; set; }
}

public class ImageDbEntity : CreatedBySomeoneDbEntity
{
    [Required]
    public Uri Url { get; set; }
    public List<int> Tags { get; set; }

    public ICollection<VgnItmImageDbEntity> VgnItmImages { get; set; } = new List<VgnItmImageDbEntity>();
}

public abstract class CreatedBySomeoneDbEntity : DbEntity<int>
{
    public string CreatedById { get; set; }
    public UserDbEntity CreatedBy { get; set; }
    public string UpdatedById { get; set; }
    public UserDbEntity UpdatedBy { get; set; }
}

public abstract class DbEntity<TId>
{
    [Key]
    public TId Id { get; set; }
    public DateTime CreatedDate { get; set; }
    public DateTime UpdatedDate { get; set; }
}

    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    { 

        var uriConverter = new ValueConverter<Uri, string>(
            v => v == null ? null : v.ToString(),
            v => v == null ? null : new Uri(v)
            );

        modelBuilder.Entity<ImageDbEntity>(image =>
        {
            image.Property(u => u.CreatedDate)
                .HasDefaultValueSql("CURRENT_TIMESTAMP");
            image.Property(u => u.UpdatedDate)
                .HasDefaultValueSql("CURRENT_TIMESTAMP");

            image.Property(i => i.Url)
                .HasConversion(uriConverter);
        });

        // Vegan Item Images

        modelBuilder.Entity<VgnItmImageDbEntity>(viImage => 
        { 
            viImage.HasKey(e => new { e.VgnItmId, e.ImageId }); 

            viImage.HasOne(e => e.VgnItm) 
                .WithMany(v => v.VgnItmImages) 
                .HasForeignKey(e => e.VgnItmId); 

            viImage.HasOne(e => e.Image) 
                .WithMany(i => i.VgnItmImages) 
                .HasForeignKey(e => e.ImageId); 

            viImage.HasIndex(g => new { g.VgnItmId, g.ImageId }).IsUnique(); 
            viImage.Property(e => e.Id).IsRequired().ValueGeneratedOnAdd(); 
            viImage.Property(u => u.CreatedDate) 
                .HasDefaultValueSql("CURRENT_TIMESTAMP"); 
            viImage.Property(u => u.UpdatedDate) 
                .HasDefaultValueSql("CURRENT_TIMESTAMP"); 
            viImage.HasOne(q => q.UpdatedBy) 
                .WithMany() 
                .HasForeignKey(k => k.UpdatedById); 
            viImage.HasOne(q => q.CreatedBy) 
                .WithMany() 
                .HasForeignKey(k => k.CreatedById); 
        }); 

        modelBuilder.Entity<VgnItmDbEntity>(vgnItm =>
        {
            vgnItm.HasIndex("CompanyName", "Name", "Discriminator").IsUnique();
            vgnItm.Property(e => e.Id).IsRequired().ValueGeneratedOnAdd();
            vgnItm.HasDiscriminator<string>("Discriminator")
                .HasValue<RecipeItmDbEntity>("RecipeItm")
                .HasValue<EventItmDbEntity>("EventItm")
                .HasValue<GroceryItmDbEntity>("GroceryItm")
                .HasValue<MenuItmDbEntity>("MenuItm")
                .HasValue<RestaurantItmDbEntity>("RestaurantItm")
                .HasValue<GroceryStoreItmDbEntity>("GroceryStoreItm")
                .HasValue<FashionItmDbEntity>("FashionItm");
            vgnItm.Property(u => u.CreatedDate)
                .HasDefaultValueSql("CURRENT_TIMESTAMP");
            vgnItm.Property(u => u.UpdatedDate)
                .HasDefaultValueSql("CURRENT_TIMESTAMP");
            vgnItm.HasKey(e => e.Id);
            vgnItm.HasOne(q => q.UpdatedBy)
                .WithMany()
                .HasForeignKey(k => k.UpdatedById);
            vgnItm.HasOne(q => q.CreatedBy)
                .WithMany()
                .HasForeignKey(k => k.CreatedById);
            vgnItm.HasMany(e => e.VgnItmImages)
                .WithOne()
                .OnDelete(DeleteBehavior.Cascade);
        });

        modelBuilder.Entity<RecipeItmDbEntity>(recipeItm =>
        {
            recipeItm.Property(e => e.Ingredients).HasColumnType("jsonb");
            recipeItm.Property(e => e.Method).HasMaxLength(100);
            recipeItm.Property(e => e.Tips).HasMaxLength(20);
            recipeItm.Property(e => e.Ingredients).HasMaxLength(100);
        });

        modelBuilder.Entity<SingleLocationVgnItmDbEntity>(singleLocationVgnItm =>
        {
            singleLocationVgnItm.HasIndex("CompanyName", "Name", "Discriminator", "LocationId").IsUnique();
            singleLocationVgnItm
            .HasOne(s => s.Location) // Each SingleLocationVgnItm has one Location.
            .WithMany() // A Location can be associated with many SingleLocationVgnItm.
            .HasForeignKey(s => s.LocationId); // Foreign key in SingleLocationVgnItm pointing to Location.
        });

На самом деле имя столбца — VgnItmId, а не VgnItmDbEntityId. Я не хочу, чтобы какой-либо из моих автоматически созданных столбцов EF Core содержал DbEntity в имени. Мне пришлось выполнить миграцию вручную, чтобы удалить столбец VgnItmDbEntityId, но EF Core по-прежнему считает, что он существует при выполнении запросов во время выполнения кода.

Как сообщить EF Core, что имя столбца не VgnItmDbEntityId, а VgnItmId?

Я использую EF Core 8.0.4. Я проверяю, является ли это ошибкой.

У этого парня была похожая проблема, но я уже использую его решение HasForeignKey в onModelCreating.

Было бы здорово, если бы вы предоставили минимально воспроизводимый пример.

Guru Stron 30.06.2024 08:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В VgnItmDbEntity попробуйте изменить:

vgnItm.HasMany(e => e.VgnItmImages)
    .WithOne() // potential problem
    .OnDelete(DeleteBehavior.Cascade);

К:

vgnItm.HasMany(e => e.VgnItmImages)
    .WithOne(entity => entity.VgnItm)
    .HasForeignKey(entity => entity.VgnItmId) // potentially not needed
    .OnDelete(DeleteBehavior.Cascade);

Честно говоря, в вашем коде много избыточности, во-первых, кажется, что многие отношения должны быть учтены соглашениями EF. Кроме того, у вас есть одни и те же отношения, определенные с обеих сторон (определения с одной стороны должно быть достаточно), а затем также через атрибуты (и, как было написано ранее, EF должен автоматически подбирать отношения SomePropNameId и SomePropName). Если вы не хотите полагаться на условности и быть максимально явными — в идеале придерживайтесь одного способа описания модели (свободное API или атрибуты) и описывайте взаимосвязь только «с одной стороны» (т.е. в этом случае применяйте предложенный менять и удалять viImage.HasOne(e => e.VgnItm)...).

Мне пришлось выполнить миграцию вручную, чтобы удалить столбец VgnItmDbEntityId, но EF Core по-прежнему считает, что он существует при выполнении запросов времени выполнения в коде.

Не следует этого делать. Если EF генерирует некоторую структуру — изменение имен в ней с помощью пользовательских миграций без соответствующего изменения модели не повлияет на то, как EF воспринимает базу данных.

Удивительно, это работает, спасибо! Также проведу рефакторинг с учетом ваших предложений.

BeniaminoBaggins 30.06.2024 08:29

Другие вопросы по теме