【问题标题】:Entity Framework Core code-first foreign key with optional linked entity具有可选链接实体的实体框架核心代码优先外键
【发布时间】:2022-01-09 15:45:55
【问题描述】:

我有一个文件表,我想在其他 2 个表中引用它。 每个文件都是其中一个表独有的,这意味着我的外键在至少一个链接表中没有实体,某些文件可能有一个填充键,它根本没有链接到任何实体(因为它将是稍后装箱)。

关系:(1 -> n)

Item <- File -> Image

这会导致 insert/SaveChanges 上的外键异常,因为数据库无法找到链接的实体。

我搜索了解决方案,但找不到任何解决此问题的文章,并且我提出的解决方案至少有一种代码异味。

问题:如何链接这 3 个表而不会出现数据库异常并产生代码/数据库气味?

或者可能是整个数据架构有问题,我应该尝试一些不同的东西(如果有的话)?

我想出但不想使用的解决方案:

  • 没有外键,但有一个新查询
  • 使用仅包含链接实体(文件 -> 链接 -> 项目)的中间表
  • Files 表拆分为ItemFilesImageFiles(听说这是DB 气味)

其他信息:

  • .NET Core 3.1
  • EF Core:最新
  • 数据库: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();

【问题讨论】:

  • 您还应该显示导致异常的代码。
  • 在我添加了一个没有链接的实体后,SaveChanges 上抛出了异常
  • 我假设FIleFileData?无论如何,您不要输入“没有链接”的实体。 Hashkey 是外键。
  • 但是我需要一个包含所有文件及其哈希键的列表(出于算法原因),并且我想通过哈希键将项目链接到此列表。由于并非所有文件都是项目/图像,因此我正在寻找一种解决方案来使用 EF Core 表示此链接。如果这是一个外键或不是次要的,我只是在寻找正确的解决方案来表示这种关系。这意味着,不存储“未链接”的文件是不可能的。

标签: c# sqlite .net-core entity-framework-core ef-core-3.1


【解决方案1】:

一般来说,您的 FileData 实体应包含 Guid 外键到 ItemImageData 实体,而不仅仅是导航属性,即 Guid ItemIdGuid 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 中同时配置 ItemImageData 时,这个配置应该足够了

 builder.HasMany(item => item.Files)
        .WithOne(file => file.Item)
        .IsRequired(false)
        .OnDelete(DeleteBehavior.NoAction);

【讨论】:

  • 问题是我必须在数据库编辑时检查所有链接的项目/文件,我想通过使用哈希来自动化。最后我的解决方案是放弃外键,使 HashKey 唯一并被索引(在 Item & ImageData 中),并在我需要项目的文件路径时做出选择语句。这消除了错误/丢失更新的错误情况,并使所有内容自动保持最新,而不会损失太多性能
【解决方案2】:

最后我决定放弃外键,因为我不需要更新其他实体并手动启动文件查询。 似乎不可能将文件添加为属性,因为错误一直被抛出(I tried this solution)

使用的约束列表: Item.Id => 键 Item.Hash => 索引,唯一

Image.Id => 键 Image.Hash => 索引,唯一

File.Id => 键 File.Hash => 索引,不是唯一的

我决定保留 Item 和 Image 的 ID 的原因之一是其他实体主要通过 ID 引用这些,而不是哈希,并且哈希可能会更改,需要大量更新。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-22
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多