Как объявить отношения внешнего ключа с помощью Code First Entity Framework (4.1) в MVC3?

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

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

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

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

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

С уважением,

Гвидо

2 ответов


если у вас Order class, добавляя свойство, которое ссылается на другой класс В вашей модели, например 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 или fluent 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. Это обязательное свойство (не пустой). Если вы просто не установите его, он, скорее всего, попытается отправить значение по умолчанию в базу данных. Значение по умолчанию-0, поэтому если у вас нет клиента с id = 0 вы получите исключение.