#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. Большое спасибо! Я думаю, что это решило проблему. Не знаю, что я сделал не так, что привело к этому дополнительному идентификатору категории, но теперь, похоже, все работает нормально, так что это спорно. Еще раз спасибо.