Как я должен объявить отношения внешнего ключа, используя 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, вы получите исключение.

Другие вопросы по тегам