【问题标题】:EF Core - create relationship without primary/foreign keysEF Core - 创建没有主键/外键的关系
【发布时间】:2020-10-03 14:55:14
【问题描述】:

我正在尝试将 EF 配置为在检索用户或产品时包含文档。实体 Document 有一个 ReferenceId 属性,它应该存储 UserId 或 ProductId。这样,当我为用户或产品保存文档时,UserId 或 ProductId 会保存到 Document.ReferenceId

实体:

公共类用户
{
    公共字符串 ID { 获取;放; }
    公共 ICollection 文档 { 获取;放; }
}

公开课产品
{
    公共字符串 ID { 获取;放; }
    公共 ICollection 文档 { 获取;放; }
}

公共类文档
{
    公共字符串 ID { 获取;放; }
    公共字符串 ReferenceId { 获取;放; }
}

配置:

builder.Entity(e =>
{
    e.HasKey(e => e.Id);
    e.Property(p => p.Id).ValueGeneratedOnAdd();
    e.HasMany(e => e.Documents)
     .WithOne()
     .OnDelete(DeleteBehavior.Cascade);
});

builder.Entity(e =>
{
    e.HasKey(e => e.Id);
    e.Property(p => p.Id).ValueGeneratedOnAdd();
    e.HasMany(e => e.Documents)
     .WithOne()
     .OnDelete(DeleteBehavior.Cascade);
});

builder.Entity(e =>
{
    e.HasKey(e => e.Id);
    e.Property(p => p.Id).ValueGeneratedOnAdd();
    e.ToTable("文档");
});

保存:

var 用户 = 新用户 { };
var userDocument = new Document { ReferenceId = user.Id };

var 产品 = 新产品 { };
var productDocument = new Document { ReferenceId = product.Id };

_context.Users.Add(user);
_context.Products.Add(product);
_context.Add(userDocument);
_context.Add(productDocument);
_context.SaveChanges();

迁移:

migrationBuilder.CreateTable(
    名称:“文件”,
    列:表 => 新
    {
        Id = table.Column(可为空:false),
        ReferenceId = table.Column(可为空: true),
        ProductId = table.Column(可为空: true),
        UserId = table.Column(可为空:true)
    },
    约束:表 =>
    {
        table.PrimaryKey("PK_Documents", x => x.Id);
        表.外键(
            名称:“FK_Documents_Products_ProductId”,
            列:x => x.ProductId,
            principalTable: "产品",
            主体列:“ID”,
            onDelete: 参考动作.Cascade);
        表.外键(
            名称:“FK_Documents_Users_UserId”,
            列:x => x.UserId,
            principalTable: "用户",
            主体列:“ID”,
            onDelete: 参考动作.Cascade);
    });

我不想在 Documents 表上创建 2 个外键(ProductId 和 UserId)。有没有办法让 EF 自动将 UserId 和 ProductId 链接到 ReferenceId?

【问题讨论】:

  • 试着想象一下这在纯 SQL 中应该是什么样子。一个字段不可能是多个表的 FK。这就是臭名昭著的多态关联(反)模式。
  • 嗨格特,我同意你的看法。我不想有任何FK。我想要的是找到一种方法(如果可能的话)让 EF 自动将 User.Id 和 Product.Id 映射到 Document.ReferenceId (所以我不需要手动分配值)。检索记录时也是如此。
  • 不确定如何创建没有外键的相关实体。拥有外键并不意味着您必须手动分配它们
  • 您实际上是在让 EF 读懂您的想法。不幸的是,这不受支持。
  • 您可以尝试在 Documents 属性上添加 [NotMapped]。所以你不会在你的迁移文件上看到外键......我认为最简单的方法是,即使这样,它也不是一个很好的数据库结构......

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


【解决方案1】:

解决这个问题的正确方法是让 User 和 Product 继承一个基类并将 Id 和 Documents 属性移动到该类。

public class BaseObject
{
    public string Id { get; set; }
    public ICollection<Document> Documents { get; set; }
}

public class User : BaseObject
{
}

public class Product : BaseObject
{
}

public class Document
{
    public string BaseObjectId { get; set; }
}

【讨论】:

  • 您好,埃里克,感谢您的回答。这仍然会在 Documents 上创建 2 个外键(ProductId 和 UserId)。
【解决方案2】:

我看到的唯一方法是使用 TPH 继承 (See here for more information)。

我已经引用并编辑了answer by Erik H

public enum DocumentType
{
    User = 0,
    Product = 1
}

public class BaseObject
{
    public string Id { get; set; }
    public ObjectType DocumentType{ get; set; }
    public virtual ICollection<Document> Documents { get; set; }
}

public class User : BaseObject
{
}

public class Product : BaseObject
{
}

public class Document
{
    public int Id { get; set; }
    public string BaseObjectId { get; set; }
    public virtual BaseObject DocumentObject { get; set; }
}

您可以通过 fluent-Api 设置鉴别器。这样 ef core 只会为 ProductUser 两个对象创建一个表,并通过鉴别器列的值来区分它们的类型。但前提是它们具有与基类共享的完全相同的属性。一旦您向其中一个子类添加属性,就会创建一个新表(包含来自基类和子类的所有属性)。 下面是判别器的配置:

modelBuilder.Entity<BaseObject>()
    .HasDiscriminator<DocumentType>("DocumentType")
    .HasValue<User>(DocumentType.User)
    .HasValue<Product>(DocumentType.Product)

这可能不是一个干净的方法(对我来说,似乎 User 和 Product 不应该从同一个基类继承,因为它们除了与文档的关系之外不共享任何东西)。但它应该可以按您的意愿工作。

【讨论】:

    【解决方案3】:

    您可以创建多对多表:

    public class Product
    {
      public string Id { get; set; }
      public ICollection<ProductDocument> ProductDocuments{ get; set; }
    }
    
    public class Document
    {
      public string ReferenceId { get; set; }
    }
    
    public class ProductDocument
    {
      public ICollection<Product> Products{ get; set; }
      public ICollection<Document> Documents { get; set; }
    }
    

    您必须使用相同的模式为您的用户表创建一个单独的表,即UserDocumentes

    【讨论】:

    • 嗨文斯,这不是我要找的。谢谢。
    猜你喜欢
    • 2022-11-02
    • 2021-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-02
    • 2011-07-23
    • 2015-09-06
    • 2020-12-10
    相关资源
    最近更新 更多