Я воспроизвел простой пример проблемы, возникшей у меня с Entity Framework.
Я хочу иметь три таблицы:
Users, Projects, WorkOrders
Таблица Users
содержит информацию о пользователях для всех других таблиц (в примере только две). WorkOrders
имеет информацию о том, какой User
должен работать над этим рабочим заданием и к которому Project
принадлежит.
Вот классы:
public class User
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<WorkOrder> WorkOrders { get; set; }
public virtual ICollection<Project> Projects { get; set; }
}
public class Project
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int ManagerId { get; set; }
public DateTime Start { get; set; }
public virtual User Manager { get; set; }
public virtual ICollection<WorkOrder> WorkOrders { get; set; }
}
public class WorkOrder
{
[Key]
public int Id { get; set; }
public int Number { get; set; }
public string Type { get; set; }
public int AssigneeId { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public virtual User Assignee { get; set; }
}
Когда я пытаюсь запустить программу, она выдает исключение:
'Введение ограничения FOREIGN KEY' FK_dbo.WorkOrders_dbo.Projects_ProjectId 'в таблицу' WorkOrders 'может вызвать циклы или несколько каскадных путей. Укажите ON DELETE NO ACTION или ON UPDATE NO ACTION или измените другие ограничения FOREIGN KEY.
Тогда я пошел другим путем. Я попробовал DB-первый подход с EF. Сначала я создал таблицы и соединения в SQL Server Management Studio:

Тогда сгенерированные EF модели выглядят почти так же, как и мои, с подходом, основанным на коде.
public partial class User
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public User()
{
this.Project = new HashSet<Project>();
this.WorkOrder = new HashSet<WorkOrder>();
}
public int Id { get; set; }
public string Name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Project> Project { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<WorkOrder> WorkOrder { get; set; }
}
public partial class Project
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Project()
{
this.WorkOrder = new HashSet<WorkOrder>();
}
public int Id { get; set; }
public string Name { get; set; }
public int ManagerId { get; set; }
public System.DateTime Start { get; set; }
public virtual User User { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<WorkOrder> WorkOrder { get; set; }
}
public partial class WorkOrder
{
public int Id { get; set; }
public int Number { get; set; }
public string Type { get; set; }
public int AssigneeId { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public virtual User User { get; set; }
}
Таким образом, код практически идентичен, за исключением SuppressMesages
и конструкторов в классах WorkOrder
и User
. Второй подход работает.
Я хотел бы знать, в чем разница? Также контекстный класс идентичен моему. Где или как определяются эти ограничения FK или настройки каскадного удаления?