Как я должен объявлять отношения с внешними ключами с использованием структуры кода первого объекта (4.1) в MVC3?

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

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

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

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

Итак, в частности, я хотел бы исключение возникает, когда я пытаюсь сохранить объект Order без указав объект клиента. Эта похоже, не поведение, если я просто составьте объекты, как описано в большинстве документов 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 по свойству "Пациент" по типу "PhysicianPortal.Models.Order" не действительный. Имя внешнего ключа "Родитель" не было найдено на зависимом типе 'PhysicianPortal.Models.Order. Значение имени должно быть разделено запятой список имен свойств внешнего ключа.

Привет,

Гвидо

Ответ 1

Если у вас есть класс 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, чтобы включить Lazy Loading.

2) Создайте свойство с именем Parent, которое будет служить в качестве внешнего ключа. В этом случае, вероятно, имеет смысл вызвать его, например, ParentID (вам также нужно будет изменить имя в ForeignKeyAttribute):

public int ParentID { get; set; }

По моему опыту в этом случае, хотя лучше работать с ним по-другому:

[ForeignKey("Patient")]
public int ParentID { get; set; }

public virtual Patient Patient { get; set; }

Ответ 2

Вы можете определить внешний ключ:

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 является свойством внешнего ключа и определяет требуемое отношение между дочерним и существующим родителем. Сохранение дочернего элемента без exsiting родителя будет генерировать исключение.

Если ваше имя свойства FK не состоит из имени свойства навигации и родительского имени PK, вы должны либо использовать аннотацию данных ForeignKeyAttribute, либо свободный API для сопоставления отношения

Аннотации данных:

// The name of related navigation property
[ForeignKey("Parent")]
public int ParentId { get; set; }

Fluent API:

modelBuilder.Entity<Child>()
            .HasRequired(c => c.Parent)
            .WithMany(p => p.Childs)
            .HasForeignKey(c => c.ParentId);

Другие типы ограничений могут выполняться с помощью аннотаций данных и проверки модели.

Edit:

Вы получите исключение, если вы не установите ParentId. Требуется свойство (не имеет значения NULL). Если вы просто не установите его, скорее всего, попробуете отправить значение по умолчанию в базу данных. Значение по умолчанию равно 0, поэтому, если у вас нет клиента с Id = 0, вы получите исключение.