Entity Framework Code First - два внешних ключа из одной таблицы
Я только начал использовать код EF, поэтому я начинающий в этой теме.
Я хотел создать отношения между командами и матчами:
1 матч = 2 команды (дома, в гостях) и результат.
Я думал, что легко создать такую модель, поэтому я начал кодировать:
public class Team
{
[Key]
public int TeamId { get; set;}
public string Name { get; set; }
public virtual ICollection<Match> Matches { get; set; }
}
public class Match
{
[Key]
public int MatchId { get; set; }
[ForeignKey("HomeTeam"), Column(Order = 0)]
public int HomeTeamId { get; set; }
[ForeignKey("GuestTeam"), Column(Order = 1)]
public int GuestTeamId { get; set; }
public float HomePoints { get; set; }
public float GuestPoints { get; set; }
public DateTime Date { get; set; }
public virtual Team HomeTeam { get; set; }
public virtual Team GuestTeam { get; set; }
}
И я получаю исключение:
Ссылочные отношения приведут к циклической ссылке, которая не допускается. [Имя ограничения = Match_GuestTeam]
Как создать такую модель с двумя внешними ключами для одной таблицы?
Ответы
Ответ 1
Попробуйте следующее:
public class Team
{
public int TeamId { get; set;}
public string Name { get; set; }
public virtual ICollection<Match> HomeMatches { get; set; }
public virtual ICollection<Match> AwayMatches { get; set; }
}
public class Match
{
public int MatchId { get; set; }
public int HomeTeamId { get; set; }
public int GuestTeamId { get; set; }
public float HomePoints { get; set; }
public float GuestPoints { get; set; }
public DateTime Date { get; set; }
public virtual Team HomeTeam { get; set; }
public virtual Team GuestTeam { get; set; }
}
public class Context : DbContext
{
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Match>()
.HasRequired(m => m.HomeTeam)
.WithMany(t => t.HomeMatches)
.HasForeignKey(m => m.HomeTeamId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Match>()
.HasRequired(m => m.GuestTeam)
.WithMany(t => t.AwayMatches)
.HasForeignKey(m => m.GuestTeamId)
.WillCascadeOnDelete(false);
}
}
Первичные ключи отображаются по умолчанию. Команда должна иметь две коллекции матчей. У вас нет единой коллекции, на которую ссылаются два FK. Матч отображается без каскадного удаления, потому что он не работает в этих ссылках со многими из многих.
Ответ 2
Также можно указать атрибут ForeignKey()
для свойства навигации:
[ForeignKey("HomeTeamID")]
public virtual Team HomeTeam { get; set; }
[ForeignKey("GuestTeamID")]
public virtual Team GuestTeam { get; set; }
Таким образом, вам не нужно добавлять код в метод OnModelCreate
Ответ 3
Я знаю это несколько лет назад, и вы можете решить свою проблему с помощью вышеупомянутого решения. Тем не менее, я просто хочу предложить использовать InverseProperty для тех, кто все еще нуждается. По крайней мере, вам не нужно ничего менять в OnModelCreating.
Следующий код не проверен.
public class Team
{
[Key]
public int TeamId { get; set;}
public string Name { get; set; }
[InverseProperty("HomeTeam")]
public virtual ICollection<Match> HomeMatches { get; set; }
[InverseProperty("GuestTeam")]
public virtual ICollection<Match> GuestMatches { get; set; }
}
public class Match
{
[Key]
public int MatchId { get; set; }
public float HomePoints { get; set; }
public float GuestPoints { get; set; }
public DateTime Date { get; set; }
public virtual Team HomeTeam { get; set; }
public virtual Team GuestTeam { get; set; }
}
Вы можете узнать больше о InverseProperty в MSDN: https://msdn.microsoft.com/en-us/data/jj591583?f=255&MSPPError=-2147217396#Relationships
Ответ 4
Вы также можете попробовать:
public class Match
{
[Key]
public int MatchId { get; set; }
[ForeignKey("HomeTeam"), Column(Order = 0)]
public int? HomeTeamId { get; set; }
[ForeignKey("GuestTeam"), Column(Order = 1)]
public int? GuestTeamId { get; set; }
public float HomePoints { get; set; }
public float GuestPoints { get; set; }
public DateTime Date { get; set; }
public virtual Team HomeTeam { get; set; }
public virtual Team GuestTeam { get; set; }
}
Когда вы делаете столбец FK, разрешите NULLS, вы нарушаете цикл. Или мы просто обманываем генератор схемы EF.
В моем случае эта простая модификация решает проблему.
Ответ 5
Это связано с тем, что Cascade Deletes включены по умолчанию. Проблема заключается в том, что при вызове delete в сущности он также удаляет все связанные с f-key объекты. Вы не должны указывать обязательные значения, чтобы устранить эту проблему. Лучшим вариантом было бы удалить правило EF Code First Cascade delete:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
Вероятно, безопаснее явно указывать, когда делать каскадное удаление для каждого из детей при сопоставлении /config. объект.
Ответ 6
InverseProperty
в EF Core делает решение простым и чистым.
InverseProperty
Таким образом, желаемое решение будет:
public class Team
{
[Key]
public int TeamId { get; set;}
public string Name { get; set; }
[InverseProperty(nameof(Match.HomeTeam))]
public ICollection<Match> HomeMatches{ get; set; }
[InverseProperty(nameof(Match.GuestTeam))]
public ICollection<Match> AwayMatches{ get; set; }
}
public class Match
{
[Key]
public int MatchId { get; set; }
[ForeignKey(nameof(HomeTeam)), Column(Order = 0)]
public int HomeTeamId { get; set; }
[ForeignKey(nameof(GuestTeam)), Column(Order = 1)]
public int GuestTeamId { get; set; }
public float HomePoints { get; set; }
public float GuestPoints { get; set; }
public DateTime Date { get; set; }
public Team HomeTeam { get; set; }
public Team GuestTeam { get; set; }
}