Как я должен объявлять отношения внешнего ключа, используя Code First Entity Framework (4.1) в MVC3? - PullRequest
94 голосов
/ 04 апреля 2011

Я безуспешно искал ресурсы для того, чтобы объявить отношения внешнего ключа и другие ограничения, используя сначала код EF 4.1. В основном я строю модель данных в коде и использую MVC3 для запроса этой модели. Все работает через MVC, что замечательно (спасибо Microsoft!), Но теперь я хочу, чтобы он НЕ работал, потому что мне нужны ограничения модели данных.

Например, у меня есть объект Order, который имеет массу свойств, которые являются внешними объектами (таблицами). Прямо сейчас я могу создать Порядок без проблем, но без возможности добавить внешний ключ или внешние объекты. MVC3 устанавливает это без проблем.

Я понимаю, что мог бы сам добавить объекты в класс контроллера перед сохранением, но я бы хотел, чтобы вызов DbContext.SaveChanges () завершился неудачей, если отношения ограничений не были выполнены.

НОВАЯ ИНФОРМАЦИЯ

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

СООБЩЕНИЕ ОБ ОШИБКЕ

Атрибут ForeignKey для свойства «Пациент» по типу PhysicianPortal.Models.Order не является действительный. Название внешнего ключа «Родитель» не был найден на зависимом типе 'PhysicianPortal.Models.Order. Значение имени должно быть через запятую список имен свойств внешнего ключа.

С уважением,

Guido

Ответы [ 2 ]

154 голосов
/ 05 апреля 2011

Если у вас есть класс 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; }
29 голосов
/ 05 апреля 2011

Вы можете определить внешний ключ следующим образом:

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, вы получите исключение.

...