Когда этот код запускается (к вашему сведению, общий <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.StateMachineBox
1.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__7
2.MoveNext() в Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.<MoveNextAsync>d__20.MoveNext() at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable
1.ConfiguredValueTaskAwaiter.GetResult() в Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.d__151.MoveNext() at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleOrDefaultAsync>d__15
1.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.
В 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 воспринимает базу данных.
Удивительно, это работает, спасибо! Также проведу рефакторинг с учетом ваших предложений.
Было бы здорово, если бы вы предоставили минимально воспроизводимый пример.