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

时间:2020-06-14 00:41:41

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

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

实体:

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

配置:

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

保存:

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();

迁移:

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

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

3 个答案:

答案 0 :(得分:2)

解决该问题的正确方法是让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; }
}

答案 1 :(得分:1)

我看到的唯一方法是使用 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 不应该从同一个基类继承,因为除了与文档的关系之外,它们不共享任何东西)。但它应该可以如你所愿。

答案 2 :(得分:0)

您可以创建多对多表格:

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)创建一个单独的表。