データ検証Data Validation

注意

Ef 4.1 以降のみ -このページで説明した機能、api などは、Entity Framework 4.1 で導入されました。EF4.1 Onwards Only - The features, APIs, etc. discussed in this page were introduced in Entity Framework 4.1. 以前のバージョンを使用している場合、情報の一部またはすべてが適用されないIf you are using an earlier version, some or all of the information does not apply

このページの内容は、最初にジュリー Lerman () によって作成された記事から適用され https://thedatafarm.com ます。The content on this page is adapted from an article originally written by Julie Lerman (https://thedatafarm.com).

Entity Framework には、クライアント側の検証のためにユーザーインターフェイスに渡すことができる、またはサーバー側の検証に使用する、さまざまな検証機能が用意されています。Entity Framework provides a great variety of validation features that can feed through to a user interface for client-side validation or be used for server-side validation. Code first を使用する場合は、注釈または fluent API 構成を使用して検証を指定できます。When using code first, you can specify validations using annotation or fluent API configurations. 追加の検証 (さらに複雑) は、コードで指定できます。また、モデルが code first、model first、database first のいずれから susan されているかにかかわらず機能します。Additional validations, and more complex, can be specified in code and will work whether your model hails from code first, model first or database first.

ModelThe model

ここでは、簡単なクラスのペア (ブログと Post) で検証について説明します。I'll demonstrate the validations with a simple pair of classes: Blog and Post.

public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string BloggerName { get; set; }
    public DateTime DateCreated { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public DateTime DateCreated { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
    public ICollection<Comment> Comments { get; set; }
}

データの注釈Data Annotations

Code First は、 System.ComponentModel.DataAnnotations コードの最初のクラスを構成する1つの手段として、アセンブリの注釈を使用します。Code First uses annotations from the System.ComponentModel.DataAnnotations assembly as one means of configuring code first classes. これらの注釈の中には、、などの規則を提供する注釈があり Required MaxLength MinLength ます。Among these annotations are those which provide rules such as the Required, MaxLength and MinLength. 多くの .NET クライアントアプリケーションでは、これらの注釈も認識しています。たとえば、ASP.NET MVC です。A number of .NET client applications also recognize these annotations, for example, ASP.NET MVC. これらの注釈を使用して、クライアント側とサーバー側の両方の検証を実現できます。You can achieve both client side and server side validation with these annotations. たとえば、"ブログのタイトル" プロパティを強制的に必須のプロパティにすることができます。For example, you can force the Blog Title property to be a required property.

[Required]
public string Title { get; set; }

アプリケーションで追加のコードやマークアップの変更を加えなくても、既存の MVC アプリケーションは、プロパティと注釈の名前を使用してメッセージを動的に構築しながら、クライアント側の検証を実行します。With no additional code or markup changes in the application, an existing MVC application will perform client side validation, even dynamically building a message using the property and annotation names.

図 1

この Create view のポストバックメソッドでは、新しいブログをデータベースに保存するために Entity Framework が使用されますが、アプリケーションがそのコードに到達する前に MVC のクライアント側の検証がトリガーされます。In the post back method of this Create view, Entity Framework is used to save the new blog to the database, but MVC's client-side validation is triggered before the application reaches that code.

ただし、クライアント側の検証は箇条書きではありません。Client side validation is not bullet-proof however. ユーザーはブラウザーの機能に影響を与える可能性がありますが、さらに悪いことに、ハッカーは UI 検証を回避するために trickery を使用することがあります。Users can impact features of their browser or worse yet, a hacker might use some trickery to avoid the UI validations. ただし、Entity Framework も Required 注釈を認識して検証します。But Entity Framework will also recognize the Required annotation and validate it.

これをテストする簡単な方法は、MVC のクライアント側の検証機能を無効にすることです。A simple way to test this is to disable MVC's client-side validation feature. これは、MVC アプリケーションの web.config ファイルで行うことができます。You can do this in the MVC application's web.config file. AppSettings セクションには、ClientValidationEnabled のキーがあります。The appSettings section has a key for ClientValidationEnabled. このキーを false に設定すると、UI が検証を実行できなくなります。Setting this key to false will prevent the UI from performing validations.

<appSettings>
    <add key="ClientValidationEnabled"value="false"/>
    ...
</appSettings>

クライアント側の検証が無効になっている場合でも、アプリケーションで同じ応答が返されます。Even with the client-side validation disabled, you will get the same response in your application. "タイトルフィールドが必要です" というエラーメッセージが以前と同様に表示されます。The error message "The Title field is required" will be displayed as before. ただし、これを除き、サーバー側の検証の結果になります。Except now it will be a result of server-side validation. Entity Framework は、 Required (面倒でも、データベースに送信するコマンドを構築する前に) 注釈の検証を実行 INSERT し、メッセージを表示する MVC にエラーを返します。Entity Framework will perform the validation on the Required annotation (before it even bothers to build an INSERT command to send to the database) and return the error to MVC which will display the message.

Fluent APIFluent API

注釈ではなく code first の fluent API を使用して、同じクライアント側 & サーバー側検証を取得できます。You can use code first's fluent API instead of annotations to get the same client side & server side validation. これを使用するのではなく Required 、MaxLength 検証を使用します。Rather than use Required, I'll show you this using a MaxLength validation.

Fluent API 構成は code first で適用され、クラスからモデルを構築します。Fluent API configurations are applied as code first is building the model from the classes. DbContext クラスの OnModelCreating メソッドをオーバーライドすることによって、構成を挿入できます。You can inject the configurations by overriding the DbContext class' OnModelCreating method. 次に示すのは、ブログ Gername プロパティが10文字以内であることを指定する構成です。Here is a configuration specifying that the BloggerName property can be no longer than 10 characters.

public class BlogContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Comment> Comments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>().Property(p => p.BloggerName).HasMaxLength(10);
    }
}

Fluent API の構成に基づいてスローされた検証エラーは自動的に UI には表示されませんが、コードでキャプチャし、それに応じて応答することができます。Validation errors thrown based on the Fluent API configurations will not automatically reach the UI, but you can capture it in code and then respond to it accordingly.

次に、アプリケーションの blog コントローラークラスにおける例外処理エラーコードを示します。これにより、Entity Framework が、ブログのブログに、最大10文字を超える blog Gername を使用して保存しようとしたときに、その検証エラーがキャプチャされます。Here's some exception handling error code in the application's BlogController class that captures that validation error when Entity Framework attempts to save a blog with a BloggerName that exceeds the 10 character maximum.

[HttpPost]
public ActionResult Edit(int id, Blog blog)
{
    try
    {
        db.Entry(blog).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    catch (DbEntityValidationException ex)
    {
        var error = ex.EntityValidationErrors.First().ValidationErrors.First();
        this.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
        return View();
    }
}

検証は自動的にビューに戻されないため、を使用する追加のコードが ModelState.AddModelError 使用されています。The validation doesn't automatically get passed back into the view which is why the additional code that uses ModelState.AddModelError is being used. これにより、エラーの詳細によって、htmlhelper を使用してエラーが表示されるビューに対して、エラーの詳細が表示され ValidationMessageFor ます。This ensures that the error details make it to the view which will then use the ValidationMessageFor Htmlhelper to display the error.

@Html.ValidationMessageFor(model => model.BloggerName)

IValidatableObjectIValidatableObject

IValidatableObject はに存在するインターフェイスです System.ComponentModel.DataAnnotationsIValidatableObject is an interface that lives in System.ComponentModel.DataAnnotations. Entity Framework API には含まれていませんが、Entity Framework クラスでサーバー側の検証に使用することもできます。While it is not part of the Entity Framework API, you can still leverage it for server-side validation in your Entity Framework classes. IValidatableObject``ValidateEntity Framework が SaveChanges 中に呼び出すメソッドを提供します。または、クラスを検証するたびにを呼び出すことができます。IValidatableObject provides a Validate method that Entity Framework will call during SaveChanges or you can call yourself any time you want to validate the classes.

やなどの Required 構成 MaxLength では、1つのフィールドに対して検証を実行します。Configurations such as Required and MaxLength perform validation on a single field. メソッドでは、 Validate 2 つのフィールドの比較など、さらに複雑なロジックを使用できます。In the Validate method you can have even more complex logic, for example, comparing two fields.

次の例では、 Blog クラスはを実装するように拡張されており、 IValidatableObject とが一致しないという規則を提供してい Title BloggerName ます。In the following example, the Blog class has been extended to implement IValidatableObject and then provide a rule that the Title and BloggerName cannot match.

public class Blog : IValidatableObject
{
    public int Id { get; set; }

    [Required]
    public string Title { get; set; }

    public string BloggerName { get; set; }
    public DateTime DateCreated { get; set; }
    public virtual ICollection<Post> Posts { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Title == BloggerName)
        {
            yield return new ValidationResult(
                "Blog Title cannot match Blogger Name",
                new[] { nameof(Title), nameof(BloggerName) });
        }
    }
}

ValidationResultコンストラクターは、 string エラーメッセージと、 string 検証に関連付けられているメンバー名を表すの配列を表すを受け取ります。The ValidationResult constructor takes a string that represents the error message and an array of strings that represent the member names that are associated with the validation. この検証 Title ではとの両方がチェックされるため BloggerName 、両方のプロパティ名が返されます。Since this validation checks both the Title and the BloggerName, both property names are returned.

Fluent API によって提供される検証とは異なり、この検証結果はビューによって認識され、前にエラーを追加するために使用した例外ハンドラー ModelState は不要です。Unlike the validation provided by the Fluent API, this validation result will be recognized by the View and the exception handler that I used earlier to add the error into ModelState is unnecessary. では両方のプロパティ名を設定しているため、 ValidationResult これらのプロパティの両方について、MVC HtmlHelpers によってエラーメッセージが表示されます。Because I set both property names in the ValidationResult, the MVC HtmlHelpers display the error message for both of those properties.

図 2

DbContext. ValidateEntityDbContext.ValidateEntity

DbContext には、というオーバーライド可能なメソッドがあり ValidateEntity ます。DbContext has an overridable method called ValidateEntity. を呼び出すと SaveChanges 、状態がではないキャッシュ内の各エンティティに対して、Entity Framework がこのメソッドを呼び出し Unchanged ます。When you call SaveChanges, Entity Framework will call this method for each entity in its cache whose state is not Unchanged. ここで検証ロジックを直接配置することも、このメソッドを使用して前のセクションで追加したメソッドなどを呼び出すこともでき Blog.Validate ます。You can put validation logic directly in here or even use this method to call, for example, the Blog.Validate method added in the previous section.

次に、新しいを検証して ValidateEntity Post 、投稿タイトルが既に使用されていないことを確認するオーバーライドの例を示します。Here's an example of a ValidateEntity override that validates new Posts to ensure that the post title hasn't been used already. まず、エンティティが post であるかどうか、およびその状態が追加されているかどうかを確認します。It first checks to see if the entity is a post and that its state is Added. その場合は、データベースを調べて、同じタイトルの投稿が既に存在するかどうかを確認します。If that's the case, then it looks in the database to see if there is already a post with the same title. 既存の投稿が既に存在する場合は、新しい DbEntityValidationResult が作成されます。If there is an existing post already, then a new DbEntityValidationResult is created.

DbEntityValidationResult``DbEntityEntry ICollection<DbValidationErrors> 1 つのエンティティに対してとを格納します。DbEntityValidationResult houses a DbEntityEntry and an ICollection<DbValidationErrors> for a single entity. このメソッドの開始時に、 DbEntityValidationResult がインスタンス化され、検出されたすべてのエラーがコレクションに追加され ValidationErrors ます。At the start of this method, a DbEntityValidationResult is instantiated and then any errors that are discovered are added into its ValidationErrors collection.

protected override DbEntityValidationResult ValidateEntity (
    System.Data.Entity.Infrastructure.DbEntityEntry entityEntry,
    IDictionary<object, object> items)
{
    var result = new DbEntityValidationResult(entityEntry, new List<DbValidationError>());

    if (entityEntry.Entity is Post post && entityEntry.State == EntityState.Added)
    {
        // Check for uniqueness of post title
        if (Posts.Where(p => p.Title == post.Title).Any())
        {
            result.ValidationErrors.Add(
                    new System.Data.Entity.Validation.DbValidationError(
                        nameof(Title),
                        "Post title must be unique."));
        }
    }

    if (result.ValidationErrors.Count > 0)
    {
        return result;
    }
    else
    {
        return base.ValidateEntity(entityEntry, items);
    }
}

明示的な検証のトリガーExplicitly triggering validation

を呼び出すと SaveChanges 、この記事で説明されているすべての検証がトリガーされます。A call to SaveChanges triggers all of the validations covered in this article. ただし、に依存する必要はありません SaveChangesBut you don't need to rely on SaveChanges. アプリケーション内の他の場所でも検証することをお勧めします。You may prefer to validate elsewhere in your application.

DbContext.GetValidationErrors では、すべての検証がトリガーされます。これは、注釈または Fluent API によって定義された検証、で作成された検証 IValidatableObject (たとえば Blog.Validate )、およびメソッドで実行された検証 DbContext.ValidateEntity です。DbContext.GetValidationErrors will trigger all of the validations, those defined by annotations or the Fluent API, the validation created in IValidatableObject (for example, Blog.Validate), and the validations performed in the DbContext.ValidateEntity method.

次のコードは GetValidationErrors 、の現在のインスタンスでを呼び出し DbContext ます。The following code will call GetValidationErrors on the current instance of a DbContext. ValidationErrors は、エンティティ型によってにグループ化され DbEntityValidationResult ます。ValidationErrors are grouped by entity type into DbEntityValidationResult. このコードは、最初に DbEntityValidationResult メソッドによって返されたを通じて、次に内部で反復処理し DbValidationError ます。The code iterates first through the DbEntityValidationResults returned by the method and then through each DbValidationError inside.

foreach (var validationResult in db.GetValidationErrors())
{
    foreach (var error in validationResult.ValidationErrors)
    {
        Debug.WriteLine(
            "Entity Property: {0}, Error {1}",
            error.PropertyName,
            error.ErrorMessage);
    }
}

検証を使用する場合のその他の考慮事項Other considerations when using validation

Entity Framework 検証を使用する場合は、次の点を考慮する必要があります。Here are a few other points to consider when using Entity Framework validation:

  • 遅延読み込みは検証中に無効になりますLazy loading is disabled during validation
  • EF は、マップされていないプロパティ (データベース内の列にマップされていないプロパティ) のデータ注釈を検証します。EF will validate data annotations on non-mapped properties (properties that are not mapped to a column in the database)
  • 検証は、の間に変更が検出された後に実行され SaveChanges ます。Validation is performed after changes are detected during SaveChanges. 検証中に変更を行う場合は、変更トラッカーに通知する必要があります。If you make changes during validation it is your responsibility to notify the change tracker
  • DbUnexpectedValidationException 検証中にエラーが発生した場合にスローされますDbUnexpectedValidationException is thrown if errors occur during validation
  • クラスにデータ注釈がない場合や、EF デザイナーを使用してモデルを作成した場合でも、モデルに含まれている Entity Framework ファセット (最大長、必須など) によって検証が行われます。Facets that Entity Framework includes in the model (maximum length, required, etc.) will cause validation, even if there are no data annotations in your classes and/or you used the EF Designer to create your model
  • 優先順位の規則:Precedence rules:
    • Fluent API 呼び出しは、対応するデータ注釈をオーバーライドしますFluent API calls override the corresponding data annotations
  • 実行順序:Execution order:
    • プロパティの検証は、型の検証の前に行われますProperty validation occurs before type validation
    • 型の検証は、プロパティの検証が成功した場合にのみ発生しますType validation only occurs if property validation succeeds
  • プロパティが複雑な場合、その検証にも次の内容が含まれます。If a property is complex, its validation will also include:
    • 複合型プロパティのプロパティレベルの検証Property-level validation on the complex type properties
    • 複合型の検証を含む複合型の型レベルの検証 IValidatableObjectType level validation on the complex type, including IValidatableObject validation on the complex type

まとめSummary

Entity Framework の検証 API は、MVC でのクライアント側の検証に非常に適していますが、クライアント側の検証に依存する必要はありません。The validation API in Entity Framework plays very nicely with client side validation in MVC but you don't have to rely on client-side validation. Entity Framework は、code first Fluent API で適用した DataAnnotations または構成のサーバー側での検証を処理します。Entity Framework will take care of the validation on the server side for DataAnnotations or configurations you've applied with the code first Fluent API.

また、インターフェイスを使用するか、メソッドをタップするかにかかわらず、動作をカスタマイズするための拡張ポイントがいくつか表示されて IValidatableObject DbContext.ValidateEntity います。You also saw a number of extensibility points for customizing the behavior whether you use the IValidatableObject interface or tap into the DbContext.ValidateEntity method. DbContextまた、Code First、Model First または Database First のワークフローを使用して概念モデルを記述するかどうかによって、これらの検証の2つの方法がで利用できます。And these last two means of validation are available through the DbContext, whether you use the Code First, Model First or Database First workflow to describe your conceptual model.