EF 4.1 Сопоставление наследования отношениям «многие ко многим»

Запутанная ситуация
У меня есть ситуация, когда у меня есть 2 объекта, один из которых наследуется от другого, которые необходимо сопоставить с двумя отдельными таблицами, но использование кода должно основываться на двух объектах. .

Подробнее

public class Team
{
  public virtual int Id { get; set; }
  public virtual ICollection<Employee> Members { get; set; }
}
public class Employee
{
  public virtual int Id { get; set; }
  public virtual string Name { get; set; }
  public virtual ICollection<Team> Teams { get; set; }
}

public class EmployeeInfo : Employee
{
  public virtual int Id { get; set; }
  public virtual decimal Amount { get; set; }
}

У нас есть существующая схема базы данных, в которой Employee и EmployeeInfo являются отдельными таблицами с FK между EmployeeInfo_Id и Employee_Id.

В нашей системе «менеджеры» будут добавлять сотрудников в систему с набором личной информации (больше свойств, чем указано выше), например, оплату, и добавлять их в команду. Другие области системы будут использовать объекты Team или Employee для различных других целей. Мы хотели бы иметь очень простой код, если можно выполнить сопоставление.

Когда менеджер создает нового сотрудника, код должен выглядеть примерно так:

public void Foo(string name, decimal pay)
{
  // create the employee
  var employee = new EmployeeInfo();
  employee.Name = name;
  employee.Pay = pay;

  // add him/her to the team
  _team.Employees.Add(employee);   // the idea being that consumers of the Team entity would not get the separate employee info properties

  // save the context
  _context.SaveChanges();
}

Конечным результатом будет то, что свойства EmployeeInfo вводятся в таблицу EmployeeInfo, а базовые данные о сотруднике вводятся в таблицу сотрудников и добавляются в группу через таблицу связей TeamEmployees.

Пока я пробую текущие сопоставления и получаю недопустимый столбец с именем «Дискриминатор». При простом добавлении сотрудника в команду.

public class TeamConfiguration : EntityTypeConfiguration<Team>
{
    public TeamConfiguration()
    {
        ToTable("Team");

        HasKey(t => t.Id);
        HasMany(t => t.Members).WithMany(m => m.Teams)
            .Map(m =>
                     {
                         m.MapLeftKey("Team_Id");
                         m.MapRightKey("Employee_Id");
                         m.ToTable("TeamEmployees");
                     });
    }
}

public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
    public EmployeeConfiguration()
    {
        ToTable("Employee");
        ToTable("EmployeeInfo");

        HasKey(t => t.Id);
        Property(p => p.Name);

        HasMany(m => m.Teams)
            .WithMany(t => t.Members)
            .Map(m =>
                     {
                         m.MapLeftKey("Employee_Id");
                         m.MapRightKey("Team_Id");
                         m.ToTable("TeamEmployees");
                     });
    }
}

Кроме того, если я исключаю связь «многие ко многим» между командой и сотрудником, я получаю исключение FK от Employee_Id до EmployeeInfo_Id.

Спасибо, Дж.Р.


person J.r. Hounddog    schedule 05.06.2011    source источник


Ответы (1)


Discriminator — это столбец, который добавляется в вашу таблицу, когда вы используете Table Per Hierarchy.
Я думаю, что вы ищете "Таблица по типу ( ТПТ)". Украсьте свой EmployeeInfo класс следующим образом:

[Table("EmployeeInfo")]
public class EmployeeInfo : Employee 

Или добавьте ниже к вашему мероприятию OnModelCreating:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
 ...
 modelBuilder.Entity<EmployeeInfo>().ToTable("EmployeeInfo");
 ...
}  

Или создайте следующий класс и используйте его как modelBuilder.Configurations.Add(new EmployeeInfoConfiguration()); в методе OnModelCreating:

public class EmployeeInfoConfiguration : EntityTypeConfiguration<EmployeeInfo>
{
    public EmployeeInfoConfiguration()
    {
        ToTable("EmployeeInfo");
    }
}

Это заставит EF создать таблицу EmployeeInfo с необходимыми ограничениями.

Кроме того, хорошо инициализировать свои коллекции в конструкторах ваших объектов, чтобы предотвратить нулевое исключение. Например, в классе Team:

public Team()
{
    this.Employees = new HashSet<Employee>();
}  

Я точно скопировал ваш код и изменил следующие части:

public class Team
{
    public Team()
    {
        this.Members = new HashSet<Employee>();
    }
    public virtual int Id { get; set; }
    public virtual ICollection<Employee> Members { get; set; }
}
public class Employee
{
    public Employee()
    {
     this.Teams = new HashSet<Team>();
    }

    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual ICollection<Team> Teams { get; set; }
}

[Table("EmployeeInfo")]
public class EmployeeInfo : Employee
{
    public virtual int Id { get; set; }
    public virtual decimal Amount { get; set; }
}  

В DbContext никаких изменений:

public partial class TestEntities : DbContext
{
    public DbSet<Employee> Employees { get; set; }
    public DbSet<EmployeeInfo> Employee_Info { get; set; }
    public DbSet<Team> Teams { get; set; }
}  

и ваш рабочий метод Foo:

public static void Foo(string name, decimal pay)
{
    var _team = new Team();
    var context = new TestEntities();
    context.Teams.Add(_team);

    // create the employee
    var employee = new EmployeeInfo();
    employee.Name = name;
    employee.Amount = pay;
    context.Employees.Add(employee);
    context.SaveChanges();

    // add him/her to the team
    _team.Members.Add(employee);   

    // save the context
    context.SaveChanges();
}  

Наконец, удалите часть ToTable("EmployeeInfo"); из EmployeeConfiguration, так как вы правильно упомянули об этом в своем событии создания режима.

Дополнительные сведения о подходе «Таблица на тип» см. на странице этой замечательной статьи.

person Kamyar    schedule 11.06.2011
comment
Я проверю это и посмотрю, как оно пойдет. Есть ли способ сделать то же самое без использования атрибута [Table] в EmployeeInfo? - person J.r. Hounddog; 11.06.2011
comment
Я не проверял это, но вы сможете использовать modelBuilder.Entity<EmployeeInfo>().ToTable("EmployeeInfo"); в Fluent API. - person Kamyar; 12.06.2011