【问题标题】:Immitating an ADO.NET design in Entity Framework Core在 Entity Framework Core 中模仿 ADO.NET 设计
【发布时间】:2021-03-23 13:15:10
【问题描述】:

我一直在学习 ADO.NET,然后是 EF Core。我的任务是在 C# 中创建一个数据库应用程序,首先在 ADO.NET 中,然后转换该应用程序,以便它可以使用实体框架。

这是我在 ADO.NET 中的设计,看起来运行良好。

CREATE TABLE Categories (
    Id INT IDENTITY,
    Name NVARCHAR(50) NOT NULL,
    CONSTRAINT PK_Categories
        PRIMARY KEY (Id)
)

CREATE TABLE CategoryCategories (
    ParentCategoryId INT,
    ChildCategoryId INT,
    CONSTRAINT PK_CategoryCategories
        PRIMARY KEY (ParentCategoryId, ChildCategoryId),
    CONSTRAINT FK_CategoryCategories_ParentCategoryId
        FOREIGN KEY (ParentCategoryId)
        REFERENCES Categories (Id),
    CONSTRAINT FK_CategoryCategories_ChildCategoryId
        FOREIGN KEY (ChildCategoryId)
        REFERENCES Categories (Id)
    ON DELETE CASCADE
)

CREATE TABLE Products (
    Id INT IDENTITY,
    ArticleNumber NVARCHAR(50) NOT NULL UNIQUE,
    Name NVARCHAR(50) NOT NULL,
    Description NVARCHAR(500) NOT NULL,
    Price DECIMAL(18,2) NOT NULL,
    CONSTRAINT PK_Products
        PRIMARY KEY (Id)
);

CREATE TABLE Products_Categories (
    ProductId INT,
    CategoryId INT NOT NULL,
    CONSTRAINT PK_Products_Categories
        PRIMARY KEY (ProductId, CategoryId),
    CONSTRAINT FK_ProdCats_Prods
        FOREIGN KEY (ProductId)
        REFERENCES Products (Id),
    CONSTRAINT FK_ProdCats_Cats
        FOREIGN KEY (CategoryId)
        REFERENCES Categories (Id)
    ON DELETE CASCADE
);

Products 和 Categories 之间的连接 - 没问题,只是两个响应类中的每一个中的 ICollection 属性。 Entity Framework 立即创建了 CategoryProducts 联结表。

在实体与其自身之间创建连接时,我遇到了障碍。我尝试简单地将迁移生成的 CategoryProducts 的所有模型构建代码复制到 CategoryCategories 的 OnModelCreating 方法中,并插入列和表的正确名称。

没有用,收到有关 CategoryCategories 处于影子模式的投诉。我已经对互联网进行了真空清洁,试图找到解决方案,但我找不到任何明确的说明。在实体框架中执行此操作真的比在常规 SQL 或 ADO.NET 中更难吗?我认为实体框架应该让事情变得更容易。

有什么建议吗?如果您需要更多信息,请告诉我。

编辑

为了清楚起见,我想我可能会添加这些类。

    class Category
    {
        public int Id { get; protected set; }
        [Required]
        public string Name { get; protected set; }

        public ICollection<Product> Products { get; protected set; }
        // public ICollection<Category> ChildCategories { get; protected set; }
        public Category(string name)
        {
            Name = name;
            Products = new List<Product>();
            // ChildCategories = new List<Category>();
        }
    }

class CategoryCategory
    {
        // EFC forced me to have an Id. Could not run Add-migr Initial without it
        public int Id { get; protected set; }
        [Required]
        public int ParentCategoryId { get; protected set; }
        [Required]
        public int ChildCategoryId { get; protected set; }
        public CategoryCategory(int parentCategoryId, int childCategoryId)
        {
            ParentCategoryId = parentCategoryId;
            ChildCategoryId = childCategoryId;
        }
    }

    class Product
    {
        public int Id { get; protected set; }
        [Required]
        public string ArticleNumber { get; protected set; }
        [Required]
        // EFC forcing me to have unprotected set
        // Without it, I can not update articles :(
        public string Name { get; set; }
        [Required]
        public string Description { get; set; }
        [Required]
        public decimal Price { get; set; }

        public ICollection<Category> Categories { get; protected set; }

        public Product(string articleNumber, string name, string description, decimal price)
        {
            ArticleNumber = articleNumber;
            Name = name;
            Description = description;
            Price = price;
            Categories = new List<Category>();
        }

        public Product(int id, string articleNumber, string name, string description, decimal price)
            : this(articleNumber, name, description, price)
        {
            Id = id;
        }
    }

对于这些类,EF 说 实体类型“CategoryCategories”处于阴影状态。一个有效的模型要求所有实体类型都有对应的 CLR 类型。

如果我取消注释 Category 中的 ICollection ChildCategories 部分,则 EF 会在 CategoryCategories 中创建第三列 CategoryId,这在我看来是非常不可取的。当我已经有两个类别时,为什么还要第三个 ID 指代类别?

【问题讨论】:

  • 实体与自身之间建立联系的目的是什么?顺便说一句,只要要求明确,EF 就会大大变得容易。
  • 目的是创建上表CategoryCategories。或者更确切地说,让 EF 创建它。正如我所说,我想在 EF 中复制 ADO.NET 设计,因此 EF 表将具有与第一个 (ADO.NET) 表完全相同的结构。
  • 如你所见,它有两个外键都指向 go Categories。

标签: entity-framework-core


【解决方案1】:

Products 和 Categories 之间的连接 - 没问题,只是两个响应类中的每一个中的 ICollection 属性。 Entity Framework 立即创建了 CategoryProducts 联结表。

如果这可行,那么您使用的是 EF Core 5.0+,它添加了对带有隐式连接实体的多对多关系的支持,可以看出,创建这种关系非常容易。

但是你的CategoryCategories代表了完全相同的关系类型,唯一的区别是两个相关的端都是同一个实体。

那么,如何在两个实体之间建立这种关系?为此,您可以在每个相关实体中添加 2(!) 个集合导航属性:

class Product
{
    public ICollection<Category> Categories { get; set; }
}

class Category
{
    public ICollection<Product> Products { get; set; }
}

(旁注:属性设置器的类型(公共、内部、受保护、私有)对于 EF Core 无关紧要)。

这将我们引向问题的答案,您如何定义同一实体之间的这种关系?好吧,完全一样 - 通过在每个相关实体中添加 2(!) 个集合导航属性,在本例中是一个并且相同:

class Category
{
    public ICollection<Category> ParentCategories { get; set; }
    public ICollection<Category> ChildCategories { get; set; }
}

就是这样。不需要显式的CategoryCategory 实体。 EF Core 会自动创建类似这样的内容

migrationBuilder.CreateTable(
    name: "CategoryCategory",
    columns: table => new
    {
        ChildCategoriesId = table.Column<int>(type: "int", nullable: false),
        ParentCategoriesId = table.Column<int>(type: "int", nullable: false)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_CategoryCategory", x => new { x.ChildCategoriesId, x.ParentCategoriesId });
        table.ForeignKey(
            name: "FK_CategoryCategory_Categories_ChildCategoriesId",
            column: x => x.ChildCategoriesId,
            principalTable: "Categories",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);
        table.ForeignKey(
            name: "FK_CategoryCategory_Categories_ParentCategoriesId",
            column: x => x.ParentCategoriesId,
            principalSchema: "SO14",
            principalTable: "Categories",
            principalColumn: "Id",
            onDelete: ReferentialAction.Restrict);
    });

如果您不喜欢生成的表/列名,它们都可以通过 fluent API 进行配置。如果愿意,您甚至可以创建和映射显式连接实体,并且仍然可以使用所谓的“跳过导航”的好处。

但是这两个集合导航属性是最低限度的,并且是使 EF Core 方法更容易的东西。

【讨论】:

  • 非常感谢!我认为这解决了它。不知道我做错了什么,导致了这个额外的 CategoryId,但现在一切似乎都正常,所以这没有实际意义。再次感谢。
猜你喜欢
  • 2021-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-18
  • 2010-09-12
  • 2021-12-02
  • 1970-01-01
相关资源
最近更新 更多