简体   繁体   English

EF Core - 创建没有主键/外键的关系

[英]EF Core - create relationship without primary/foreign keys

I'm trying to configure EF to include documents when retriving a user or product.我正在尝试将 EF 配置为在检索用户或产品时包含文档。 The entity Document has a ReferenceId property which should store either UserId or ProductId.实体 Document 有一个 ReferenceId 属性,它应该存储 UserId 或 ProductId。 This way, when I save a document for a user or product, the UserId or ProductId is saved to Document.ReferenceId .这样,当我为用户或产品保存文档时,UserId 或 ProductId 将保存到Document.ReferenceId

Entities:实体:

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

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

public class Document
{
    public string Id { get; set; }
    public string ReferenceId { get; set; }
}

Configuring:配置:

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

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

builder.Entity<Document>(e =>
{
    e.HasKey(e => e.Id);
    e.Property(p => p.Id).ValueGeneratedOnAdd();
    e.ToTable("Documents");
});

Saving:保存:

var user = new User { };
var userDocument = new Document { ReferenceId = user.Id };

var product = new Product { };
var productDocument = new Document { ReferenceId = product.Id };

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

Migrations:迁移:

migrationBuilder.CreateTable(
    name: "Documents",
    columns: table => new
    {
        Id = table.Column<string>(nullable: false),
        ReferenceId = table.Column<string>(nullable: true),
        ProductId = table.Column<string>(nullable: true),
        UserId = table.Column<string>(nullable: true)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_Documents", x => x.Id);
        table.ForeignKey(
            name: "FK_Documents_Products_ProductId",
            column: x => x.ProductId,
            principalTable: "Products",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);
        table.ForeignKey(
            name: "FK_Documents_Users_UserId",
            column: x => x.UserId,
            principalTable: "Users",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);
    });

I don't want 2 foreign keys (ProductId and UserId) to be created on Documents table.我不想在 Documents 表上创建 2 个外键(ProductId 和 UserId)。 Is there a way to make EF automatically link UserId and ProductId to ReferenceId?有没有办法让 EF 自动将 UserId 和 ProductId 链接到 ReferenceId?

The proper way to solve it would be to have User and Product inherit a base class and move the Id and Documents properties to that class.解决此问题的正确方法是让 User 和 Product 继承基础 class 并将 Id 和 Documents 属性移动到该 class。

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; }
}

The only way I see, is to use TPH inheritance ( See here for more information ).我看到的唯一方法是使用 TPH inheritance( 有关更多信息,请参见此处)。

I have quoted and edited the answer by Erik H .我引用并编辑了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; }
}

Via fluent-Api you can set a discriminator.通过 fluent-Api 你可以设置一个鉴别器。 This way ef core will only create one table for for both objects Product and User and distinguishes their type by the value of the discriminator column.这样 ef core 只会为对象ProductUser创建一个表,并通过鉴别器列的值来区分它们的类型。 But only as long as they have exactly the same properties which they share from the base class.但只要它们具有与基础 class 共享的完全相同的属性。 As soon as you add properties to one of those subclasses a new table will be created (with all properties from the base- and subclass).一旦您向其中一个子类添加属性,就会创建一个新表(包含来自基类和子类的所有属性)。 Here is the configuration for the discriminator:这是鉴别器的配置:

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

This may not be a clean approach (for me it seems like User and Product should not inherit from the same base class, because they do not share anything than the relations to documents).这可能不是一个干净的方法(对我来说,用户和产品似乎不应该从同一个基础 class 继承,因为它们除了与文档的关系之外不共享任何东西)。 But it should work as you want it.但它应该按你的意愿工作。

You can create a many to many table:您可以创建多对多表:

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; }
}

You will have to create a separate table for your user table ie UserDocumentes using the same pattern.您必须为您的用户表创建一个单独的表,即使用相同模式的UserDocumentes

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM