Как я должен объявить отношения внешнего ключа, используя Code First Entity Framework (4.1) в MVC3?
Я искал ресурсы о том, как объявлять связи с внешним ключом и другие ограничения, используя код EF 4.1 без особой удачи. В основном я строю модель данных в коде и использую MVC3 для запроса этой модели. Все работает через MVC, что замечательно (спасибо Microsoft!), Но теперь я хочу, чтобы это НЕ работало, потому что мне нужны ограничения модели данных.
Например, у меня есть объект Order, который имеет массу свойств, которые являются внешними объектами (таблицами). Прямо сейчас я могу создать Порядок без проблем, но без возможности добавить внешний ключ или внешние объекты. MVC3 устанавливает это без проблем.
Я понимаю, что я мог бы просто добавить объекты в класс контроллера перед сохранением, но я хотел бы, чтобы вызов DbContext.SaveChanges() завершился неудачей, если отношения ограничений не были выполнены.
НОВАЯ ИНФОРМАЦИЯ
Поэтому, в частности, я хотел бы, чтобы возникла исключительная ситуация, когда я пытаюсь сохранить объект Order без указания объекта customer. Это не похоже на поведение, если я просто создаю объекты, как описано в большинстве документов Code First EF.
Последний код:
public class Order
{
public int Id { get; set; }
[ForeignKey( "Parent" )]
public Patient Patient { get; set; }
[ForeignKey("CertificationPeriod")]
public CertificationPeriod CertificationPeriod { get; set; }
[ForeignKey("Agency")]
public Agency Agency { get; set; }
[ForeignKey("Diagnosis")]
public Diagnosis PrimaryDiagnosis { get; set; }
[ForeignKey("OrderApprovalStatus")]
public OrderApprovalStatus ApprovalStatus { get; set; }
[ForeignKey("User")]
public User User { get; set; }
[ForeignKey("User")]
public User Submitter { get; set; }
public DateTime ApprovalDate { get; set; }
public DateTime SubmittedDate { get; set; }
public Boolean IsDeprecated { get; set; }
}
Это ошибка, которую я получаю сейчас при доступе к сгенерированному представлению VS для пациента:
СООБЩЕНИЕ ОБ ОШИБКЕ
Недопустимый атрибут ForeignKeyAttribute для свойства "Patient" типа "PhysicianPortal.Models.Order". Имя внешнего ключа "Родитель" не найдено в зависимом типе "PhysicianPortal.Models.Order". Значение Name должно быть разделенным запятыми списком имен свойств внешнего ключа.
С Уважением,
Guido
2 ответа
Если у вас есть Order
класс, добавляя свойство, которое ссылается на другой класс в вашей модели, например Customer
должно быть достаточно, чтобы EF знал, что там есть отношения:
public class Order
{
public int ID { get; set; }
// Some other properties
// Foreign key to customer
public virtual Customer Customer { get; set; }
}
Вы всегда можете установить FK
отношение явно:
public class Order
{
public int ID { get; set; }
// Some other properties
// Foreign key to customer
[ForeignKey("Customer")]
public string CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
ForeignKeyAttribute
Конструктор принимает строку в качестве параметра: если вы помещаете ее в свойство внешнего ключа, оно представляет имя связанного свойства навигации. Если вы поместите его в свойство навигации, оно будет представлять имя связанного внешнего ключа.
Что это значит, если вы, где разместить ForeignKeyAttribute
на Customer
свойство, атрибут будет принимать CustomerID
в конструкторе:
public string CustomerID { get; set; }
[ForeignKey("CustomerID")]
public virtual Customer Customer { get; set; }
РЕДАКТИРОВАТЬ на основе последнего кода Вы получаете эту ошибку из-за этой строки:
[ForeignKey("Parent")]
public Patient Patient { get; set; }
EF будет искать свойство под названием Parent
использовать его в качестве средства защиты внешнего ключа. Вы можете сделать 2 вещи:
1) Удалить ForeignKeyAttribute
и заменить его на RequiredAttribute
отметить отношение как необходимое:
[Required]
public virtual Patient Patient { get; set; }
Украшение собственности с RequiredAttribute
также имеет хороший побочный эффект: отношение в базе данных создается с ON DELETE CASCADE
,
Я также рекомендовал бы сделать собственность virtual
включить отложенную загрузку.
2) Создайте свойство с именем Parent
это будет служить внешним ключом. В этом случае, вероятно, имеет больше смысла называть это, например, ParentID
(вам нужно изменить имя в ForeignKeyAttribute
также):
public int ParentID { get; set; }
По моему опыту в этом случае, хотя лучше работать с другой стороны:
[ForeignKey("Patient")]
public int ParentID { get; set; }
public virtual Patient Patient { get; set; }
Вы можете определить внешний ключ:
public class Parent
{
public int Id { get; set; }
public virtual ICollection<Child> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
// This will be recognized as FK by NavigationPropertyNameForeignKeyDiscoveryConvention
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Теперь ParentId является свойством внешнего ключа и определяет обязательное отношение между дочерним и существующим родительским. Спасение ребенка без ожидания родителя вызовет исключение.
Если имя вашего свойства FK не состоит из имени свойства навигации и имени родительского PK, вы должны либо использовать аннотацию данных ForeignKeyAttribute, либо свободный API для сопоставления отношения
Аннотация данных:
// The name of related navigation property
[ForeignKey("Parent")]
public int ParentId { get; set; }
Свободный API:
modelBuilder.Entity<Child>()
.HasRequired(c => c.Parent)
.WithMany(p => p.Childs)
.HasForeignKey(c => c.ParentId);
Другие типы ограничений могут быть применены аннотациями данных и проверкой модели.
Редактировать:
Вы получите исключение, если не установите ParentId
, Требуется свойство (не обнуляемое). Если вы просто не установите его, он, скорее всего, попытается отправить значение по умолчанию в базу данных. Значение по умолчанию 0, поэтому если у вас нет клиента с Id = 0, вы получите исключение.