Как спроектировать вложенные классы в .NET Core API, чтобы получить доступ к определенной схеме?

#c# #asp.net-core #asp.net-web-api #entity-framework-core

#c# #asp.net-core #asp.net-web-api #entity-framework-core

Вопрос:

Я впервые работаю с Entity Framework в API .NET Core (используя последнюю версию .NET Core).

На данный момент у меня есть 2 класса:

 public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Gender { get; set; }
    public DateTime JoinedCompany { get; set; }
    public ICollection<Skill> Skills { get; set; }
}

public class Skill
{
    public int Id { get; set; }
    public string SkillName { get; set; }
}
  

У меня также есть DBContext класс с

 public DbSet<Employee> Employees { get; set; }
public DbSet<Skill> Skills { get; set; }
  

и я могу получить это с помощью следующего:

 public async Task<IReadOnlyList<Employee>> GetEmployeesAsync () {
            return await _context.Employees
            .Include(p => p.Skills)
            .ToListAsync();
        }
  

Теперь я использую Entity Framework migrations для создания таблиц. Используя существующие классы, я получаю следующие таблицы две таблицы:

(1) «Сотрудники» со столбцами: Идентификатор, имя, фамилия, пол, JoinedCompany

(2) «Навыки» работы со столбцами: Идентификатор, SkillName, EmployeeID

Я заполнил эти таблицы некоторыми данными и получаю обратно следующее, когда нажимаю на одну из конечных точек API в postman:

 [
    {
        "firstName": "Jess",
        "lastName": "Turner",
        "gender": "F",
        "joinedCompany": "2018-10-04T00:00:00",
        "skills": [
            {
                "skillName": "T-SQL",
                "id": 1
            },
            {
                "skillName": "Angular",
                "id": 2
            },
            {
                "skillName": "Vue",
                "id": 3
            },
            {
                "skillName": "Redux",
                "id": 4
            }
        ],
        "id": 1
    },
    {
        "firstName": "Olivia",
        "lastName": "Lu",
        "gender": "F",
        "joinedCompany": "2015-03-31T00:00:00",
        "skills": [
            {
                "skillName": "Hadoop",
                "id": 5
            },
            {
                "skillName": "Azure Databricks",
                "id": 6
            },
            {
                "skillName": "Spark",
                "id": 7
            },
            {
                "skillName": "Python",
                "id": 8
            },
            {
                "skillName": "T-SQL",
                "id": 9
            }
        ],
        "id": 2
    },
    {
        "firstName": "Jonah",
        "lastName": "Smythe",
        "gender": "M",
        "joinedCompany": "2020-08-11T00:00:00",
        "skills": [],
        "id": 3
     }
]
  

Теперь я пытаюсь выяснить, как я могу спроектировать классы так, чтобы при запуске create database с использованием entity Framework в итоге у меня получалось три таблицы:

(1) «Сотрудники» со столбцами: Идентификатор, имя, фамилия, пол, JoinedCompany

(1) «EmployeeSkills» со столбцами: Идентификатор, EmployeeID, SkillId

(2) «Навыки» работы со столбцами: Идентификатор, имя навыка

Причина в том, что я не хочу сохранять строку «SkillName» снова и снова для каждого раза, когда она появляется для сотрудника. Я просто хочу использовать идентификатор. Это также изменило бы GetEmployeesAsync метод, который я применил выше. Спасибо.

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

1. Я бы попробовал поместить ICollection<Employee> в ваш класс навыков, чтобы сделать его «многие ко многим».

2. Смотрите Отношения — Многие ко многим

Ответ №1:

 public class Employee
{
    public int Id { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Gender { get; set; }

    public DateTime JoinedCompany { get; set; }

    public ICollection<EmployeeSkill> Skills { get; set; } = new HashSet<EmployeeSkill>();
}

public class Skill
{
    public int Id { get; set; }

    public string SkillName { get; set; }

    public ICollection<EmployeeSkill> Emploees { get; set; } = new HashSet<EmployeeSkill>();
}

public class EmployeeSkill
{
    public int EmployeeId { get; set; }

    public Employee Employee { get; set; }

    public int SkillId { get; set; }

    public Skill Skill { get; set; }
}

public class MyDbContext : DbContext
{

    public DbSet<Employee> Employees { get; set; }

    public DbSet<Skill> Skills { get; set; }

    public DbSet<EmployeeSkill> EmployeesSkills { get; set; }
}
  

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

1. Спасибо. Просто для продолжения, как теперь должен выглядеть мой метод GetEmployeesAsync? Я пытался return await _context.Employees.Include(p => p.EmployeesSkills).Include(p => p.Skill).ToListAsync(); , но я просто получаю следующую ошибку: Lambda expression used inside Include is not valid

2. это зависит от того, что вам нужно в качестве результата в конце: var employeesNamesWithSkills = DbContext.Employees . Выберите (x => new { x.FirstName, x.LastName, Skills = x.Skills . Выберите (x => x.Умение. Имя навыка) . ToList() }) .ToList();

3. или var skillsNamesWithEmployees = DbContext.Skills . Выберите (x => new { x.SkillName, Employees = x.Employees . Выберите (x => new { x.Employee. Имя, x.Employee. LastName }) .ToList() }) .GroupBy(x => x.SkillName) . ToDictionary(x => x.Key, y => y.SelectMany(x => x.Employees). ToList());

4. Я думаю, что по умолчанию включена отложенная загрузка, это означает, что все связанные данные будут включены автоматически, когда вы материализуете результат, ваш пример не работает, потому что вы можете включать только объекты, которые определены как свойства в родительском и не являются коллекцией — как этот DbContext.Employees . Включить (p => p.Навыки). ToListAsync().Результат