Подтвердить что ты не робот

EF Code-First Индивидуальное отношение: множественность недействительна в Role * in relationship

Я пытаюсь сделать следующее:

public class class1
{
    public int Id {get;set;}
    [ForeignKey("Class2")]
    public int Class2Id {get;set;}
    public virtual Class2 Class2 {get;set;}
}

public class class2
{
    public int Id { get; set;}
    [Required]
    public virtual int Class1Id {get;set;}
    [Required]
    [ForeignKey("Class1Id")]
    public Class1 Class1 {get;set;}
}

Однако каждый раз, когда я пытаюсь перенести мою базу данных, я получаю следующую ошибку:

Class1_Class2_Target:: множественность недействительна в роли "Class2_Class1_Target" в отношении "Class2_Class1". Поскольку свойства зависимой роли не являются ключевыми свойствами, верхняя граница множественности зависимой роли должна быть "*".

Что может быть здесь?

4b9b3361

Ответ 1

Ваша модель не является ассоциацией 1:1. Вы все еще можете иметь много объектов Class2, относящихся к одному и тому же объекту Class1. Кроме того, ваша модель не гарантирует, что объект Class2, ссылающийся на Class1, также ссылается на этот объект Class1 - Class1 может ссылаться на любой объект Class2.

Как настроить 1:1?

Общий способ гарантировать (сорт) ассоциации 1:1 в SQL состоит в том, чтобы иметь таблицу для основного объекта и одну для зависимого объекта, где первичный ключ в зависимой таблице также является внешним ключом для принципала

1:1

(Здесь Class1 - главный)

Теперь в реляционной базе данных это все еще не гарантирует ассоциацию 1:1 (поэтому я сказал "вроде" ). Это ассоциация 1: 0..1. Может быть Class1 без Class2. По правде говоря, подлинные ассоциации 1:1 невозможны в SQL, потому что нет языковой конструкции, которая синхронно вставляет две строки в разные таблицы. 1: 0..1 является ближайшим.

Свободное сопоставление

Для моделирования этой ассоциации в EF вы можете использовать свободный API. Здесь стандартный способ сделать это:

class Class1Map : EntityTypeConfiguration<Class1>
{
    public Class1Map()
    {
        this.HasKey(c => c.Id);
        this.Property(c => c.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        this.HasRequired(c1 => c1.Class2).WithRequiredPrincipal(c2 => c2.Class1);
    }
}

И в контексте:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new Class1Map());
}

И это осталось от ваших классов:

public class Class1
{
    public int Id {get;set;}
    public virtual Class2 Class2 {get;set;}
}

public class Class2
{
    public int Id {get;set;}
    public virtual Class1 Class1 {get;set;}
}

Невозможно настроить альтернативные свойства внешнего ключа в модели, потому что единственный задействованный FK должен быть зависимым первичным ключом.

Странная вещь в этой модели заключается в том, что EF не мешает вам создавать (и сохранять) объект Class1 без Class2. Я думаю, что EF должен быть способен проверить это требование перед сохранением изменений, но, по-видимому, это не так. Аналогичным образом существуют способы удалить объект Class2, не удаляя его родителя Class1. Таким образом, пара HasRequired - WithRequired не такая строгая, как выглядит (и должна быть).

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

Единственный способ получить это право в коде - это аннотации данных. (Конечно, модель базы данных все равно не сможет обеспечить соблюдение 1:1)

public class Class1
{
    public int Id {get;set;}
    [Required]
    public virtual Class2 Class2 {get;set;}
}

public class Class2
{
    [Key, ForeignKey("Class1")]
    public int Id {get;set;}
    [Required]
    public virtual Class1 Class1 {get;set;}
}

Аннотация [Key, ForeignKey("Class1")] сообщает EF, что Class1 является основным объектом.

Аннотации данных играют роль во многих API, что может быть бичем, поскольку каждый API выбирает свой собственный подмножество для реализации, но здесь он пригодится, потому что теперь EF использует не только их для разработки модели данных, но также для проверки сущностей. Теперь, если вы попытаетесь сохранить объект Class1 без Class2, вы получите ошибку проверки.

Ответ 2

У меня была такая же проблема. Я хотел бы, чтобы схема БД имела 2 таблицы, которые перекрестно ссылаются друг на друга с [foreign key] → [primary key]. Наконец я нашел способ: Скажем, у нас есть 2 класса: книги и авторы. Класс Book должен иметь внешний ключ к автору, который его создал, а класс Author должен иметь внешний ключ к последней книге, которую он написал. Путь к тому, чтобы EF понял это, используя первый код: (Обратите внимание, что это делается с использованием смеси аннотаций данных и свободного API)

public class Book {
    ...
    public Guid BookId
    ...
    public Guid AuthorId { get; set; }

    [ForeignKey("AuthorId")]
    public virtual Author author { get; set; }
}

public class Author {
    ...
    public Guid AuthorId
    ...
    public Guid? LatestBookId { get; set; }

    [ForeignKey("LatestBookId")]
    public virtual Book book { get; set; }

    public virtual ICollection<Book> books { get; set; }
}

// using fluent API
class BookConfiguration : EntityTypeConfiguration<Book> {

    public BookConfiguration() {
        this.HasRequired(b => b.author)
            .WithMany(a => a.books);
    }

}

Это работает и создает точную схему БД, которую я хотел. В SQL он создавал бы таблицы и внешние ключи, соответствующие следующему коду:

CREATE TABLE [dbo].[Book](
    [BookId] [uniqueidentifier] NOT NULL,
    [AuthorId] [uniqueidentifier] NOT NULL,
    ...
 CONSTRAINT [PK_dbo.Book] PRIMARY KEY CLUSTERED 
(
    [BookId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

...

GO

ALTER TABLE [dbo].[Book] WITH CHECK ADD  CONSTRAINT [FK_dbo.Book.Author_AuthorId] FOREIGN KEY([AuthorId])
REFERENCES [dbo].[Author] ([AuthorId])
GO

...

CREATE TABLE [dbo].[Author](
    [AuthorId] [uniqueidentifier] NOT NULL,
    [LatestBookId] [uniqueidentifier] NULL,
    ...
 CONSTRAINT [PK_dbo.Author] PRIMARY KEY CLUSTERED 
(
    [AuthorId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

...

GO

ALTER TABLE [dbo].[Author]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Author_dbo.Book_LatestBookId] FOREIGN KEY([LatestBookId])
REFERENCES [dbo].[Book] ([BookId])
GO

...

Ответ 3

Один из двух классов должен быть создан перед другим и, следовательно, требует аннотации [Обязательный]. Если Class2 зависит от Class1, укажите [Required, ForeignKey ( "Class1" )]. Вы также можете использовать свободный API для его настройки в своем классе контекста.