Microsoft のチュートリアル「10 行でズバリ!! ASP.NET MVC を構成する各コンポーネントとネーミング ルール (C#)」のように、既存の SQL Server データベースから Entity Data Model (EDM) を生成して ASP.NET MVC アプリで使用する場合、ユーザー入力を検証するためのデータアノテーション属性をどのように付与できるかを書きます。

上の画像は、SQL Server 2008 Express のデータベース AdventureWorksLT をベースに、Visual Studio 2015 + EF6 で生成した EDM (Model1.edmx) です。(注:Visual Studio 2010 + EF4 で生成した EDM は内容が異なりますが、データアノテーション属性を付与する方法は同じです)
生成した EDM をベースに、上に紹介した「10 行でズバリ!!」チュートリアルと同様に、Visual Studio のスキャフォールディング機能を利用して、Address テーブルを表示・編集する ASP.NET MVC アプリの Controller と View を一式自動生成する場合を例に考えます。
自動生成された EDM で Address テーブルを表す Model のコードは、上の画像で示すように Address.cs にあります。そのコードは以下の通りです(説明に不要な部分は省略しています)。
namespace AdventureWorksLT
{
using System;
using System.Collections.Generic;
public partial class Address
{
//・・・コンストラクタ(省略)・・・
public int AddressID { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string StateProvince { get; set; }
public string CountryRegion { get; set; }
public string PostalCode { get; set; }
public System.Guid rowguid { get; set; }
public System.DateTime ModifiedDate { get; set; }
//・・・ナビゲーションプロパティ(省略)・・・
}
}
これに手を加えればよさそうに思えますが、それは NG です。何故なら、コードのコメントに書いてあるように、"このファイルを手動で変更すると、アプリケーションで予期しない動作が発生する可能性があります。このファイルに対する手動の変更は、コードが再生成されると上書きされます" ということだからです。
ではどうするかですが、Microsoft の文書「Validation with the Data Annotation Validators (C#)」の「Using Data Annotation Validators with the Entity Framework」のセクションに書いてある方法を取るのがよさそうです。(日本語版は機械翻訳なので英文を読むことをお勧めします)
TechNet の記事を読めばこれ以降の説明は不要かもしれませんが、記事がリンク切れになったりすると困るので、上の Address クラスの場合を例に方法を書いておきます。
具体的には以下のようなコードを EDM とは別の場所にクラスファイルを追加してそれに含めます。上の画像のソリューションエクスプローラの AddressMetaData.cs がそのクラスファイルです。
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace AdventureWorksLT
{
[MetadataType(typeof(AddressMetaData))]
public partial class Address
{
}
public class AddressMetaData
{
[Display(Name = "住所1")]
[Required(ErrorMessage = "{0} は必須入力です")]
public object AddressLine1 { get; set; }
[Display(Name = "住所2")]
public object AddressLine2 { get; set; }
[Display(Name = "街")]
[Required(ErrorMessage = "{0} は必須入力です")]
[StringLength(10, ErrorMessage = "{0} は {1} 以内。")]
public object City { get; set; }
[Display(Name = "州")]
[Required(ErrorMessage = "{0} は必須入力です")]
public object StateProvince { get; set; }
[Display(Name = "国名")]
[Required(ErrorMessage = "{0} は必須入力です")]
public object CountryRegion { get; set; }
[Display(Name = "郵便番号")]
[Required(ErrorMessage = "{0} は必須入力です")]
public object PostalCode { get; set; }
[Display(Name = "GUID")]
[Required(ErrorMessage = "{0} は必須入力です")]
public object rowguid { get; set; }
[Display(Name = "更新日")]
[Required(ErrorMessage = "{0} は必須入力です")]
public object ModifiedDate { get; set; }
}
}
上のコードで AddressMetaData が TechNet の記事で言うメタデータクラスです。それにプロキシプロパティを定義して必要なデータアノテーション属性を付与しています。
TechNet の記事にも書いてありますが、プロキシプロパティの型は自動生成された Address クラスのプロパティの型と同じである必要はないそうです(同じでも OK ですが)。上のコードでは TechNet の記事にならってプロキシプロパティの型は Object 型にしています。
自動生成された Address クラスは partial として定義されているところに注目してください。partial なので、上のコードのように別のクラスファイルに Address クラスを定義してそれに MetadataTypeAttribute Classを付与することができます。(自動生成された Address クラスに手を加える必要はありません)
MetadataType 属性の引数に typeof(AddressMetaData) を設定することにより、メタデータクラス AddressMetaData を Address クラスに関連付けています。
以上の設定により、データアノテーション属性による検証が、クライアント側とサーバー側の両方で行われるようになります。
さらに、Display 属性も追加していますので、表示名が Display 属性の Name プロパティで設定した通りになります。