私は次のことをしようとしています:
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'のロール 'Class2_Class1_Target'では無効です。依存ロールプロパティはキープロパティではないため、依存ロールの多重度の上限は「*」である必要があります。
ここで何が問題になりますか?
モデルは1:1の関連付けではありません。同じone _Class2
_オブジェクトを参照するmany _Class1
_オブジェクトを持つことができます。また、モデルは_Class2
_を参照する_Class1
_がこの_Class1
_オブジェクトによって参照されることも保証しません— _Class1
_は_Class2
_を参照できますオブジェクト。
SQLで1:1の関連付けを保証する一般的な方法は、principalエンティティのテーブルと、dependentエンティティのテーブルを用意することです。従属テーブルもプリンシパルの外部キーです。
(ここでは_Class1
_がプリンシパルです)
現在、リレーショナルデータベースでは、これはまだ1:1の関連付けを保証していません(だから「並べ替え」と言いました)。 1:0..1アソシエーションです。 _Class1
_なしで_Class2
_を使用できます。真実は、SQLでは2つの行を異なるテーブルに同期的に挿入する言語構造がないため、SQLでは真の1:1の関連付けは不可能です。 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
_オブジェクトの作成(および保存)を止めないことですwithout a _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")]
アノテーションは、_Class1
_が主要なエンティティであることをEFに伝えます。
データ注釈は多くのAPIで役割を果たしますが、これは呪いになる可能性があります。各APIが実装する独自のサブセットを選択するためですが、ここではEFがデータをdesignに使用するだけでなく、モデルだけでなく、検証エンティティにも。 _class1
_なしで_class2
_オブジェクトを保存しようとすると、検証エラーが発生します。
私はまったく同じ問題を抱えていました。私が望んでいたのは、[外部キー]-> [主キー]で相互参照する2つのテーブルを持つDBスキーマです。最後に、私は方法を見つけました: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);
}
}
これは機能し、必要な正確なDBスキーマを作成します。 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
...
2つのクラスの一方を他方の前に作成する必要があるため、[Required]アノテーションが必要です。 Class2がClass1に依存している場合、[Required、ForeignKey( "Class1")]を指定します。また、流れるようなAPIを使用して、コンテキストクラスでこれを構成することもできます。