Entity Framework 带有可选链接实体的核心代码优先外键
Entity Framework Core code-first foreign key with optional linked entity
我有 table 个文件,我想在另外 2 个 table 中引用这些文件。
每个文件都是这些 tables 之一独有的,这意味着我的外键在至少 linked tables 之一中没有实体,一些文件可能有link 已填充的密钥根本没有实体(因为它将在稍后创建)。
关系:(1 -> n)
Item <- File -> Image
这会导致插入/SaveChanges
时出现外键异常,因为数据库无法找到 linked 实体。
我搜索了一个解决方案,但找不到任何解决此问题的文章,我想出的解决方案至少有一种代码味道。
问题:我如何 link 这 3 table 没有得到数据库异常并产生 code/db 气味?
或者是整个数据架构有问题,我应该尝试一些不同的东西(如果是的话)?
我想到但不想使用的解决方案:
- 没有外键,只有一个新查询
- 使用中介 table,它只有 linked 实体的实体(文件 -> link -> 项目)
- 将
Files
table 拆分为 ItemFiles
和 ImageFiles
(我听说这是 DB 的味道)
其他信息:
- .NET 核心 3.1
- EF 核心:最新
- 数据库:Sqlite
缩短的模型:
public class FileData
{
public Item Item { get; set; }
public ImageData Image { get; set; }
public Guid Id { get; set; }
public string HashKey { get; set; }
// ...
}
public class Item
{
public FileData[] Files { get; set; }
public Guid Id { get; set; }
public string HashKey { get; set; }
// ...
}
public class ImageData
{
public FileData[] Files { get; set; }
public Guid Id { get; set; }
public string HashKey { get; set; }
// ...
}
数据库配置:
public class FileDataConfiguration : IEntityTypeConfiguration<FileData>
{
public void Configure(EntityTypeBuilder<FileData> builder)
{
builder.HasKey(file => file.Id);
builder.HasIndex(file => file.HashKey);
// ...
}
}
public class ItemConfiguration : IEntityTypeConfiguration<Item>
{
public void Configure(EntityTypeBuilder<Item> builder)
{
builder.HasKey(item => item.Id);
builder.HasMany(item => item.Files)
.WithOne(file => file.Item)
.IsRequired(false)
.HasForeignKey(file => file.HashKey)
.IsRequired(false)
.HasPrincipalKey(item => item.HashKey);
builder.HasIndex(file => file.HashKey);
// ...
}
}
public class ImageDataConfiguration : IEntityTypeConfiguration<ImageData>
{
public void Configure(EntityTypeBuilder<ImageData> builder)
{
builder.HasKey(image => image.Id);
builder.HasMany(image => image.Files)
.WithOne(file => file.Image)
.IsRequired(false)
.HasForeignKey(file => file.HashKey)
.IsRequired(false)
.HasPrincipalKey(image => image.HashKey);
builder.HasIndex(image => image.HashKey);
// ...
}
}
这段代码抛出异常
// both examples throw an exception, independent of each other
//example 1:
dbContext.Files.Add(
new File(){
HashKey="1"
}
);
dbContext.SaveChanges();
//example 2:
dbContext.Files.Add(
new File(){
HashKey="2"
}
);
dbContext.Items.Add(
new Item(){
HashKey="2"
}
);
dbContext.SaveChanges();
一般来说,您的 FileData
实体应该包含 Guid
Item
和 ImageData
实体的外键,而不仅仅是导航属性,即 Guid ItemId
和Guid ImageId
。例如:
public class FileData
{
public Guid ItemId { get; set;}
public Item Item { get; set; }
public Guid ImageId {get; set; }
public ImageData Image { get; set; }
public Guid Id { get; set; }
public string HashKey { get; set; }
// ...
}
同样在 Fluent Api 中同时配置 Item
和 ImageData
这个配置应该足够了
builder.HasMany(item => item.Files)
.WithOne(file => file.Item)
.IsRequired(false)
.OnDelete(DeleteBehavior.NoAction);
最后我决定放弃外键,因为我不需要更新其他实体并手动启动文件查询。
添加文件 属性 似乎是不可能的,因为错误一直被抛出 .
使用的约束列表:
Item.Id => 钥匙
Item.Hash => 索引,唯一
Image.Id => 钥匙
Image.Hash => 索引,唯一
File.Id => 钥匙
File.Hash => 索引,非唯一
我决定保留 Item 和 Image 的 ID 的原因之一是其他实体主要通过 ID 引用它们,而不是哈希,并且哈希可能会更改,需要大量更新。
我有 table 个文件,我想在另外 2 个 table 中引用这些文件。 每个文件都是这些 tables 之一独有的,这意味着我的外键在至少 linked tables 之一中没有实体,一些文件可能有link 已填充的密钥根本没有实体(因为它将在稍后创建)。
关系:(1 -> n)
Item <- File -> Image
这会导致插入/SaveChanges
时出现外键异常,因为数据库无法找到 linked 实体。
我搜索了一个解决方案,但找不到任何解决此问题的文章,我想出的解决方案至少有一种代码味道。
问题:我如何 link 这 3 table 没有得到数据库异常并产生 code/db 气味?
或者是整个数据架构有问题,我应该尝试一些不同的东西(如果是的话)?
我想到但不想使用的解决方案:
- 没有外键,只有一个新查询
- 使用中介 table,它只有 linked 实体的实体(文件 -> link -> 项目)
- 将
Files
table 拆分为ItemFiles
和ImageFiles
(我听说这是 DB 的味道)
其他信息:
- .NET 核心 3.1
- EF 核心:最新
- 数据库:Sqlite
缩短的模型:
public class FileData
{
public Item Item { get; set; }
public ImageData Image { get; set; }
public Guid Id { get; set; }
public string HashKey { get; set; }
// ...
}
public class Item
{
public FileData[] Files { get; set; }
public Guid Id { get; set; }
public string HashKey { get; set; }
// ...
}
public class ImageData
{
public FileData[] Files { get; set; }
public Guid Id { get; set; }
public string HashKey { get; set; }
// ...
}
数据库配置:
public class FileDataConfiguration : IEntityTypeConfiguration<FileData>
{
public void Configure(EntityTypeBuilder<FileData> builder)
{
builder.HasKey(file => file.Id);
builder.HasIndex(file => file.HashKey);
// ...
}
}
public class ItemConfiguration : IEntityTypeConfiguration<Item>
{
public void Configure(EntityTypeBuilder<Item> builder)
{
builder.HasKey(item => item.Id);
builder.HasMany(item => item.Files)
.WithOne(file => file.Item)
.IsRequired(false)
.HasForeignKey(file => file.HashKey)
.IsRequired(false)
.HasPrincipalKey(item => item.HashKey);
builder.HasIndex(file => file.HashKey);
// ...
}
}
public class ImageDataConfiguration : IEntityTypeConfiguration<ImageData>
{
public void Configure(EntityTypeBuilder<ImageData> builder)
{
builder.HasKey(image => image.Id);
builder.HasMany(image => image.Files)
.WithOne(file => file.Image)
.IsRequired(false)
.HasForeignKey(file => file.HashKey)
.IsRequired(false)
.HasPrincipalKey(image => image.HashKey);
builder.HasIndex(image => image.HashKey);
// ...
}
}
这段代码抛出异常
// both examples throw an exception, independent of each other
//example 1:
dbContext.Files.Add(
new File(){
HashKey="1"
}
);
dbContext.SaveChanges();
//example 2:
dbContext.Files.Add(
new File(){
HashKey="2"
}
);
dbContext.Items.Add(
new Item(){
HashKey="2"
}
);
dbContext.SaveChanges();
一般来说,您的 FileData
实体应该包含 Guid
Item
和 ImageData
实体的外键,而不仅仅是导航属性,即 Guid ItemId
和Guid ImageId
。例如:
public class FileData
{
public Guid ItemId { get; set;}
public Item Item { get; set; }
public Guid ImageId {get; set; }
public ImageData Image { get; set; }
public Guid Id { get; set; }
public string HashKey { get; set; }
// ...
}
同样在 Fluent Api 中同时配置 Item
和 ImageData
这个配置应该足够了
builder.HasMany(item => item.Files)
.WithOne(file => file.Item)
.IsRequired(false)
.OnDelete(DeleteBehavior.NoAction);
最后我决定放弃外键,因为我不需要更新其他实体并手动启动文件查询。
添加文件 属性 似乎是不可能的,因为错误一直被抛出
使用的约束列表: Item.Id => 钥匙 Item.Hash => 索引,唯一
Image.Id => 钥匙 Image.Hash => 索引,唯一
File.Id => 钥匙 File.Hash => 索引,非唯一
我决定保留 Item 和 Image 的 ID 的原因之一是其他实体主要通过 ID 引用它们,而不是哈希,并且哈希可能会更改,需要大量更新。