カテゴリー
Microsoft

【ASP.NET MVC5】マスタの値が重複しないように、モデルのプロパティにインデックスユニークを付ける

ポイント

  • [Index(IsUnique=true)] という Annotation 属性をプロパティに付けることでそのカラムは一意となる。
  • プロパティの型が String の場合、MaxLength も付ける必要がある。一意性をチェックするためには、カラムへの格納サイズが無限とも思われる String だとパフォーマンスが大きく低下するためと思われる。

モデルのコード

    public class Sex
    {
        public int Id { get; set; }
        [Required]
        [Index(IsUnique=true)]
        [MaxLength(10)]
        public string Name { get; set; }
    }

もし MaxLength 属性を付けなかった場合は、どうなったか?

Add-Migration コマンドは、正常に実行できました。

ところが、Update-Database コマンドで例外となりました。

Name カラムが NVARCHAR (MAX) のまま、ユニークインデックスを付けることはできないのですね><。

PM> Update-Database -Verbose
Using StartUp project ‘Sample1’.
Using NuGet project ‘Sample1’.
Specify the ‘-Verbose’ flag to view the SQL statements being applied to the target database.
Target database is: ‘Sample1Context-20151016212614’ (DataSource: (localdb)\MSSQLLocalDB, Provider: System.Data.SqlClient, Origin: Configuration).
Applying explicit migrations: [201511121308078_AddUniqueToSexName].
Applying explicit migration: 201511121308078_AddUniqueToSexName.
CREATE UNIQUE INDEX [IX_Name] ON [dbo].Sexes
System.Data.SqlClient.SqlException (0x80131904): Column ‘Name’ in table ‘dbo.Sexes’ is of a type that is invalid for use as a key column in an index.
場所 System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
… 略 …
場所 System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
ClientConnectionId:b86455ca-327e-4622-9c29-53bf8922f2df
Error Number: 1919、State: 1、Class: 16
Column ‘Name’ in table ‘dbo.Sexes’ is of a type that is invalid for use as a key column in an index.
PM>

ユニークインデックスをつけたところ、アプリの画面から既存の値を追加しようとするとどうなったか?

性別マスタに「男」「女」とすでに登録済みの状態から、さらに「女」を追加しようとしたところ、次の例外が発生して追加はできませんでした。

予定通りとなりました♪

[SqlException (0x80131904): Cannot insert duplicate key row in object ‘dbo.Sexes’ with unique index ‘IX_Name’. The duplicate key value is (女).
The statement has been terminated.]
System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) +2418102
… 略 …

回避するにはどうしたらよいかしら?次の2案が思い浮かびますの。

  • 事前にチェックをする。
  • 例外をキャッチする。

例外の実行コストは高い(と目にしたことがあります)ことや、例外の用途を考えますと、事前にチェックをするのがよさそうですわね♪

チェックとして、独自の検証属性を作るとよいかしら?一度データベースへの問い合わせが発生しますから、少し難易度が高いかしら?

なお、自作の検証属性クラスは、以前の投稿で少し取り組んでおります。

おわりに

次のページが参考になりました!ありがとう存じます♪

以上です。

コメントを残す