Asp.net-mvc-data-annotations
ASP.NET MVC-データ注釈
DataAnnotationsは、モデルクラスを構成するために使用され、最も一般的に必要な構成を強調表示します。 DataAnnotationsは、ASP.NET MVCなどの多くの.NETアプリケーションでも理解されます。これにより、これらのアプリケーションは、クライアント側の検証に同じ注釈を活用できます。 DataAnnotation属性は、デフォルトのCode-First規則をオーバーライドします。
*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規則に準拠していないため、これを処理するためにKey属性が追加され、それが主キーになります。
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プロパティと同じように扱いますが、Code Firstによって生成されたデータベースフィールドが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に設定することと一致します。
簡単な例を見て、CourseクラスのTitleプロパティに追加してConcurrencyCheckがどのように機能するかを見てみましょう。
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プロパティに適用されます。 Code Firstでは、次のコードに示すように、楽観的な同時実行性を確認するために、更新コマンドにタイトル列が含まれます。
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; }
}
上記のStudentクラスの例では、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はCoursedテーブルにnvarchar(24)列Titleを作成します。
ユーザーが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属性で指定された長さ未満、またはMaxLength属性で指定された長さを超えるTitleプロパティの値を設定した場合、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; }
}
上記の例では、テーブルは管理スキーマで指定されています。 次のスクリーンショットに示すように、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 Cours{
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プロパティはなく、StudentクラスのStdntIDプロパティです。
これに対する解決策は、登録でナビゲーションプロパティを作成し、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は、ゲッターまたはセッターを持たないプロパティの列を作成しません。
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は\ {クラス名} _ \ {プライマリキー}外部キー列を作成します。 データベースが生成されると、次のスクリーンショットに示すように、多数の外部キーが表示されます。
ご覧のとおり、Code Firstは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; }
}
ご覧のとおり、上記のCourseクラスにInverseProperty属性が適用され、それが属するEnrollmentクラスの参照プロパティを指定すると、Code Firstはデータベースを生成し、次のスクリーンショットに示すようにEnrollmentsテーブルに2つの外部キー列のみを作成します。
理解を深めるために、上記の例を実行することをお勧めします。