Entity-framework-data-annotations
Entity Framework-データ注釈
DataAnnotationsは、最も一般的に必要な構成を強調表示するクラスを構成するために使用されます。 DataAnnotationsは、ASP.NET MVCなどの多くの.NETアプリケーションでも理解されます。これにより、これらのアプリケーションは、クライアント側の検証に同じ注釈を活用できます。 DataAnnotation属性は、デフォルトのCodeFirst規則をオーバーライドします。
*System.ComponentModel.DataAnnotations* には、列のNULL可能性またはサイズに影響を与える次の属性が含まれています。
Key
タイムスタンプ
ConcurrencyCheck
必須
最小長
MaxLength
StringLength
*System.ComponentModel.DataAnnotations.Schema* 名前空間には、データベースのスキーマに影響を与える次の属性が含まれています。
表
カラム
索引
外部キー
NotMapped
InverseProperty
Key
Entity Frameworkは、エンティティの追跡に使用するキー値を持つすべてのエンティティに依存しています。 Code Firstが依存する規則の1つは、どのプロパティがCode Firstクラスのキーであるかをどのように示唆するかです。
- 慣例では、「Id」という名前のプロパティ、またはクラス名と「StudentId」などの「Id」を組み合わせたプロパティを検索します。
- プロパティは、データベースの主キー列にマップされます。 *Student、Course、およびEnrollmentクラスはこの規則に従います。
次に、StudentクラスがIDではなくStdntIDという名前を使用したとします。 Code Firstがこの規則に一致するプロパティを見つけられない場合、キープロパティが必要であるというEntity Frameworkの要件のため、例外をスローします。 キーアノテーションを使用して、EntityKeyとして使用するプロパティを指定できます。
StdntIDを含むStudentクラスの次のコードを見てみましょう。ただし、デフォルトのCode First規則には準拠していません。 そのため、これを処理するために、キー属性を追加して、それを主キーにします。
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
アプリケーションを実行し、SQL Server Explorerでデータベースを調べると、主キーが学生テーブルのStdntIDになっていることがわかります。
Entity Frameworkは複合キーもサポートしています。* 複合キー*は、複数のプロパティで構成される主キーでもあります。 たとえば、主キーがLicenseNumberとIssuingCountryの組み合わせであるDrivingLicenseクラスがあります。
public class DrivingLicense {
[Key, Column(Order = 1)]
public int LicenseNumber { get; set; }
[Key, Column(Order = 2)]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}
複合キーがある場合、Entity Frameworkでは、キープロパティの順序を定義する必要があります。 これを行うには、列注釈を使用して順序を指定します。
タイムスタンプ
Code FirstはTimestampプロパティをConcurrencyCheckプロパティと同じように扱いますが、コードが最初に生成するデータベースフィールドがnull不可であることも保証します。
- 並行性チェックにrowversionまたはtimestampフィールドを使用するのがより一般的です。
- プロパティの型がバイト配列である限り、ConcurrencyCheckアノテーションを使用する代わりに、より具体的なTimeStampアノテーションを使用できます。
- 指定されたクラスに含めることができるタイムスタンププロパティは1つだけです。
TimeStampプロパティをCourseクラスに追加して、簡単な例を見てみましょう-
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
[Timestamp]
public byte[] TStamp { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
上記の例でわかるように、Timestamp属性はCourseクラスのByte []プロパティに適用されます。 そのため、Code FirstはCoursesテーブルにタイムスタンプ列「+ TStamp +」を作成します。
ConcurrencyCheck
ConcurrencyCheck注釈を使用すると、ユーザーがエンティティを編集または削除するときに、データベースでの同時実行チェックに使用する1つ以上のプロパティにフラグを付けることができます。 EF Designerで作業している場合、これはプロパティのConcurrencyModeをFixedに設定することと一致します。
ConcurrencyCheckがCourseクラスのTitleプロパティに追加されて動作する仕組みの簡単な例を見てみましょう。
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
public string Title { get; set; }
public int Credits { get; set; }
[Timestamp, DataType("timestamp")]
public byte[] TimeStamp { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
上記のコースクラスでは、ConcurrencyCheck属性が既存のTitleプロパティに適用されます。 これで、次のコードに示すように、コードファーストの更新コマンドにタイトル列が含まれ、オプティミスティックな同時実行性がチェックされます。
exec sp_executesql N'UPDATE [dbo].[Courses]
SET [Title] = @0
WHERE (([CourseID] = @1) AND ([Title] = @2))
',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'Maths',@1=1,@2=N'Calculus'
go
必要な注釈
Requiredアノテーションは、特定のプロパティが必須であることをEFに伝えます。 FirstMidNameプロパティにRequired idが追加されている次のStudentクラスを見てみましょう。 必須属性は、プロパティにデータがあることをEFに強制します。
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
上記の例に見られるように、必須属性はFirstMidNameとLastNameに適用されます。 したがって、次の図に示すように、Code Firstは、StudentsテーブルにNOT NULL FirstMidName列とLastName列を作成します。
MaxLength
MaxLength属性を使用すると、追加のプロパティ検証を指定できます。 ドメインクラスの文字列または配列型のプロパティに適用できます。 EF Code Firstは、MaxLength属性で指定された列のサイズを設定します。
MaxLength(24)属性がTitleプロパティに適用されている次のコースクラスを見てみましょう。
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
[MaxLength(24)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
上記のアプリケーションを実行すると、Code Firstは次の画像に示すように、CourseIdテーブルにnvarchar(24)列のタイトルを作成します。
ユーザーが24文字を超えるタイトルを設定すると、EFはEntityValidationErrorをスローします。
最小長
MinLength属性を使用すると、MaxLengthで行ったように、追加のプロパティ検証を指定することもできます。 次のコードに示すように、MinLength属性はMaxLength属性とともに使用することもできます。
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
[MaxLength(24) , MinLength(5)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
MinLength属性で指定された長さより短いTitleプロパティの値を設定した場合、またはMaxLength属性で指定された長さより長い値を設定した場合、EFはEntityValidationErrorをスローします。
StringLength
StringLengthでは、MaxLengthなどの追加のプロパティ検証を指定することもできます。 唯一の違いは、StringLength属性はDomainクラスの文字列型プロパティにのみ適用できることです。
public class Course {
public int CourseID { get; set; }
[StringLength (24)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Entity Frameworkは、StringLength属性のプロパティの値も検証します。 ユーザーが24文字を超えるタイトルを設定すると、EFはEntityValidationErrorをスローします。
表
Default Code First規則は、クラス名に類似したテーブル名を作成します。 Code Firstにデータベースを作成させ、作成するテーブルの名前も変更したい場合。 その後-
- 既存のデータベースでCode Firstを使用できます。 ただし、クラスの名前がデータベース内のテーブルの名前と一致しているとは限りません。
- テーブル属性は、このデフォルトの規則をオーバーライドします。
- EF Code Firstは、特定のドメインクラスのテーブル属性に指定された名前でテーブルを作成します。
クラスの名前がStudentである次の例を見てみましょう。慣例により、Code Firstは、これがStudentsという名前のテーブルにマッピングされることを前提としています。 そうでない場合は、次のコードに示すように、Table属性を使用してテーブルの名前を指定できます。
[Table("StudentsInfo")]
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
これで、Table属性がテーブルをStudentsInfoとして指定していることがわかります。 テーブルが生成されると、次の画像に示すようにテーブル名StudentsInfoが表示されます。
テーブル名のみを指定することはできませんが、次のコードに示すように、Table属性を使用してテーブルのスキーマを指定することもできます。
[Table("StudentsInfo", Schema = "Admin")]
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
上記の例でわかるように、テーブルはadminスキーマで指定されています。 次の図に示すように、Code FirstはAdminスキーマにStudentsInfoテーブルを作成します。
カラム
また、Table属性と同じですが、Table属性はテーブルの動作をオーバーライドしますが、Column属性はカラムの動作をオーバーライドします。 Default Code First規則は、プロパティ名に似た列名を作成します。 Code Firstにデータベースを作成させ、テーブルの列の名前を変更したい場合。 その後-
- 列属性はデフォルトの規則をオーバーライドします。
- EF Code Firstは、特定のプロパティのColumn属性に指定された名前の列を作成します。
プロパティの名前がFirstMidNameであり、慣例により、Code FirstがFirstMidNameという名前の列にマッピングされると想定している次の例を見てみましょう。
そうでない場合は、次のコードに示すように、Column属性を使用して列の名前を指定できます。
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
[Column("FirstName")]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Column属性が列をFirstNameとして指定していることがわかります。 テーブルが生成されると、次の図に示すように、列名FirstNameが表示されます。
索引
Index属性は、Entity Framework 6.1で導入されました。 以前のバージョンを使用している場合、このセクションの情報は適用されません。
- IndexAttributeを使用して、1つ以上の列にインデックスを作成できます。
- 1つ以上のプロパティに属性を追加すると、EFはデータベースを作成するときにデータベースに対応するインデックスを作成します。
- ほとんどの場合、インデックスを使用すると、データをより高速かつ効率的に取得できます。 ただし、テーブルまたはビューにインデックスをオーバーロードすると、挿入や更新などの他の操作のパフォーマンスに不快な影響を与える可能性があります。
- インデックス作成は、Entity Frameworkの新機能であり、データベースからデータをクエリするために必要な時間を短縮することにより、Code Firstアプリケーションのパフォーマンスを向上させることができます。
- Index属性を使用してデータベースにインデックスを追加し、デフォルトのUniqueおよびClustered設定をオーバーライドして、シナリオに最適なインデックスを取得できます。
- デフォルトでは、インデックスの名前はIX_ <プロパティ名>になります
クレジットのコースクラスにインデックス属性が追加されている次のコードを見てみましょう。
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Indexs属性がCreditsプロパティに適用されていることがわかります。 テーブルが生成されると、インデックスにIX_Creditsが表示されます。
デフォルトでは、インデックスは一意ではありませんが、 IsUnique 名前付きパラメータを使用して、インデックスが一意であることを指定できます。 次の例では、次のコードに示すように一意のインデックスを紹介しています。
public class Course {
public int CourseID { get; set; }
[Index(IsUnique = true)]
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
外部キー
Code First規約は、モデル内の最も一般的な関係を処理しますが、助けが必要な場合があります。 たとえば、Studentクラスのキープロパティの名前を変更すると、Enrollmentクラスとの関係に問題が発生します。
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
データベースの生成中、Code FirstはEnrollmentクラスのStudentIDプロパティを確認し、クラス名と「ID」を一致させるという規則により、Studentクラスの外部キーとして認識します。 ただし、StudentクラスにはStudentIDプロパティはありませんが、StdntIDプロパティはStudentクラスです。
これに対する解決策は、登録でナビゲーションプロパティを作成し、ForeignKey DataAnnotationを使用して、次のコードに示すように、Code Firstが2つのクラス間の関係を構築する方法を理解できるようにすることです。
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
[ForeignKey("StudentID")]
public virtual Student Student { get; set; }
}
これで、ForeignKey属性がナビゲーションプロパティに適用されていることがわかります。
NotMapped
Code Firstのデフォルトの規則では、サポートされているデータ型で、ゲッターとセッターを含むすべてのプロパティがデータベースに表されます。 しかし、これは常にアプリケーションに当てはまるわけではありません。 NotMapped属性は、このデフォルトの規則をオーバーライドします。 たとえば、StudentクラスにFatherNameなどのプロパティがある場合でも、保存する必要はありません。 次のコードに示すように、データベースに列を作成しない場合、FatherNameプロパティにNotMapped属性を適用できます。
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
[NotMapped]
public int FatherName { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
NotMapped属性がFatherNameプロパティに適用されていることがわかります。 テーブルが生成されると、FatherName列はデータベースに作成されませんが、Studentクラスに存在することがわかります。
Code Firstは、StudentクラスのAddressおよびAgeプロパティの次の例に示すように、ゲッターまたはセッターを持たないプロパティの列を作成しません。
InverseProperty
InversePropertyは、クラス間に複数の関係がある場合に使用されます。 登録クラスでは、現在のコースと以前のコースを登録した人を追跡することができます。 Enrollmentクラスに2つのナビゲーションプロパティを追加しましょう。
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course CurrCourse { get; set; }
public virtual Course PrevCourse { get; set; }
public virtual Student Student { get; set; }
}
同様に、これらのプロパティによって参照されるCourseクラスにも追加する必要があります。 Courseクラスには、現在および以前のすべての登録を含むEnrollmentクラスに戻るナビゲーションプロパティがあります。
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}
上記のクラスに示されているように、外部キープロパティが特定のクラスに含まれていない場合、Code Firstは\ {Class Name} _ \ {Primary Key}外部キー列を作成します。 データベースが生成されると、次の外部キーが表示されます。
ご覧のとおり、最初のコードは2つのクラスのプロパティを単独で一致させることができません。 登録のデータベーステーブルには、CurrCourseとPrevCourseにそれぞれ1つの外部キーが必要ですが、Code Firstは4つの外部キープロパティを作成します。
- CurrCourse _CourseID
- PrevCourse _CourseID
- Course_CourseID、および
- Course_CourseID1
これらの問題を修正するには、InversePropertyアノテーションを使用して、プロパティの配置を指定できます。
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
[InverseProperty("CurrCourse")]
public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
[InverseProperty("PrevCourse")]
public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}
ご覧のとおり、InverseProperty属性は、それが属するEnrollmentクラスの参照プロパティを指定することにより、上記のCourseクラスに適用されます。 これで、Code Firstはデータベースを生成し、次の図に示すように、Enrollmentsテーブルに2つの外部キー列のみを作成します。
理解を深めるために、上記の例を段階的に実行することをお勧めします。