Имитирующее ADO.NET проектирование в ядре Entity Framework

#entity-framework-core

#entity-framework-core

Вопрос:

Я изучал ADO.NET , а затем EF Core. Моим заданием было создать приложение базы данных на C #, сначала в ADO.NET а затем преобразуйте это приложение так, чтобы оно вместо него использовало Entity Framework.

Это был мой дизайн в 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
);
 

Связь между продуктами и категориями — без проблем, просто свойство ICollection в каждом из двух отвечающих классов. Entity Framework немедленно создала таблицу соединений CategoryProducts.

При создании соединения между сущностью и самим собой я столкнулся со стеной. Я попытался просто скопировать весь код построения модели для CategoryProducts, сгенерированный миграциями, в метод OnModelCreating для CategoryCategories и вставить правильные имена столбцов и таблиц.

Не сработало, получил жалобы на то, что CategoryCategories находится в теневом режиме. Я очистил Интернет, пытаясь найти решение, но не могу найти никаких четких инструкций. Действительно ли это намного сложнее сделать в Entity Framework, чем в обычном SQL, или ADO.NET ? Я думал, что Entity Framework должен был упростить задачу.

Есть предложения? Если вам нужна дополнительная информация, пожалуйста, дайте мне знать.

Редактировать

Подумал, что я мог бы добавить классы, для ясности.

     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.

Если я раскомментирую дочернюю часть ICollection ChildCategories в Category, то EF создаст третий столбец CategoryID в CategoryCategories, что, на мой взгляд, очень нежелательно. Зачем мне нужен третий идентификатор, относящийся к категориям, когда у меня уже есть два из них?

Комментарии:

1. Какова цель соединения между сущностью и самим собой? Кстати, EF делает это намного проще — до тех пор, пока в требованиях есть ясность.

2. Целью было создать вышеупомянутую таблицу CategoryCategories. Или, скорее, заставьте EF создать его. Как я уже сказал, я хочу воспроизвести ADO.NET дизайн в EF, поэтому EF-таблицы будут иметь точно такую же структуру, что и первая (ADO.NET ) единицы.

3. Как вы можете видеть, у него есть два внешних ключа, ОБА указывающих на категории go.

Ответ №1:

Связь между продуктами и категориями — без проблем, просто свойство 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).

Что приводит нас к ответу на вопрос, как вы определяете такие отношения между одним и тем же объектом? Ну, точно так же — путем добавления 2 (!) навигационных свойств коллекции в каждую связанную сущность, которая в данном случае является одной и той же:

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

И это все. Нет необходимости в явной CategoryCategory сущности. Ядро EF автоматически создает что-то вроде этого

 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.

Комментарии:

1. Большое спасибо! Я думаю, что это решило проблему. Не знаю, что я сделал не так, что привело к этому дополнительному идентификатору категории, но теперь, похоже, все работает нормально, так что это спорно. Еще раз спасибо.