Як я повинен декларувати закордонні ключові відносини, використовуючи Рамку першої сутності коду (4.1) в MVC3?


104

Я шукав ресурси, як оголосити зовнішні ключові відносини та інші обмеження, використовуючи код перший 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 для пацієнта:

ПОВІДОМЛЕННЯ ПРО ПОМИЛКУ

Іноземний розподіл власності "Пацієнт" типу "PhysicianPortal.Models.Order" недійсний. Ім'я іноземного ключа "Parent" не знайдено в залежності від типу "PhysicianPortal.Models.Order". Значення Name має бути відокремленим комою списком імен властивостей іноземного ключа.

З повагою,

Гвідо

Відповіді:


164

Якщо у вас є 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; }

EDIT на основі останнього коду Ви отримуєте цю помилку через цей рядок:

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

EF шукатиме майно, покликане Parentвикористовувати його в якості виконавця закордонних ключів. Ви можете зробити дві речі:

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; }

Дякую, Сергі - я додав додаткову інформацію до блокової цитати.
Гвідо Ансельмі

@Guido - я оновив свою відповідь на основі останнього редагування коду, сподіваюся, це допоможе.
Сергі Папасейт

30

Ви можете визначити зовнішній ключ:

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 не складається з імені властивості навігації та батьківського імені ПК, вам потрібно використовувати анотацію даних 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, ви отримаєте виняток.


Дякую Ладіславу - я додав додаткову інформацію до блокової цитати.
Guido Anselmi

@Ladislav. Отже, для виконання цього обмеження Я ОБОВ'ЯЗКОВО мати як посилання на Parent, так і посилання на ParentId. Це правильно? Я додам фактичний клас вище для довідки.
Гідо Ансельмі

@Guido: Це нова інформація. Ви не використовуєте властивості сторонніх ключів. За замовчуванням усі ваші навігаційні властивості обробляються як необов’язкові. Використовуйте вільне картографування, щоб відобразити їх за потребою.
Ladislav Mrnka

@Ladislav: Ще раз дякую. Я озираюся, щоб зрозуміти відмінності між використанням даних Анотації та Fluent API. Я вніс зміни до коду вище відповідно до того, що, на вашу думку, ви говорите. Це вище все, що я повинен робити? З повагою
Гідо Ансельмі

Жоден атрибут ForeignKey не визначає властивості навігації, пов'язану із властивістю іноземного ключа, або навпаки. У вас немає властивостей сторонніх ключів, тому ви не можете використовувати цей атрибут. Спробуйте використовувати Потрібний атрибут у своїх навігаційних властивостях (я його не перевіряв).
Ладислав Мрнка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.