Entitiy Framework Code Firstでデフォルト値の設定

## Add-Migration前に初期値設定ができない

カラムにデフォルト値設定なんてよくあること。
しかしながらオプションにはそれららしいものが出てこない。
資料探して読んでみるもよくわからず。

なんでdefaultvalueが存在しているのに設定できないの？？？おかしくない？？？

設定にあるんだからできるはず。やってみましょう。

## デフォルト値設定

http://stackoverflow.com/questions/19554050/entity-framework-6-code-first-default-value
defaultvalueがAdd-Migration前に設定できないのなんて先人が話題にしてないはずもなく。

雰囲気としては[前回](http://qiita.com/tomopy03/items/b535f361f04a16c1202b)と変わらなそうな感じがしますね。
規約作る→`modelBuilder.Conventions`にAddの流れで問題なさそうです。

ではまず規約を作りましょう。
今回は`HasColumnAnnotation`に設定します。

```C#
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class DefaultValueAttribute : Attribute
{
    public string DefaultValue { get; set; }
}

public class DefaultValueAttributeConvention
: PrimitivePropertyAttributeConfigurationConvention<DefaultValueAttribute>
{
    public override void Apply(ConventionPrimitivePropertyConfiguration configuration, DefaultValueAttribute attribute)
    {
        configuration.HasColumnAnnotation("DefaultValue", attribute.DefaultValue);
    }
}
```

そして`OnModelCreating`の`modelBuilder.Conventions`にAddすると。

```C#
public class TestDataContext : DbContext
{
    public DbSet<testtable> testtable { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
        modelBuilder.Conventions.Add(new DefaultValueAttributeConvention());
          
        base.OnModelCreating(modelBuilder);
    }
}
```

ここからが前回と少し違うところ。
`SqlServerMigrationSqlGenerator`を継承したSqlGeneratorを作ります。

```C#
internal class DefaultValueSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AddColumnOperation addColumnOperation)
    {
        SetAnnotatedColumn(addColumnOperation.Column);
        base.Generate(addColumnOperation);
    }

    protected override void Generate(AlterColumnOperation alterColumnOperation)
    {
        SetAnnotatedColumn(alterColumnOperation.Column);
        base.Generate(alterColumnOperation);
    }

    protected override void Generate(CreateTableOperation createTableOperation)
    {
        SetAnnotatedColumns(createTableOperation.Columns);
        base.Generate(createTableOperation);
    }

    protected override void Generate(AlterTableOperation alterTableOperation)
    {
        SetAnnotatedColumns(alterTableOperation.Columns);
        base.Generate(alterTableOperation);
    }

    private void SetAnnotatedColumn(ColumnModel col)
    {
        AnnotationValues values;
        if (col.Annotations.TryGetValue("DefaultValue", out values))
        {
            if (values.NewValue != null)
            {
                col.DefaultValueSql = (string)values.NewValue;
            }
            else
            {
                col.DefaultValueSql = null;
            }
        }
    }

    private void SetAnnotatedColumns(IEnumerable<ColumnModel> columns)
    {
        foreach (var column in columns)
        {
            SetAnnotatedColumn(column);
        }
    }
}
```

ここでは先ほど`HasColumnAnnotation`に設定した`DefaultValue`の情報を取得し、カラムのDefaultValue設定に反映しています。
いろいろ名称が被ってわかりづらいですね、申し訳ない。
この設定は`Configuration()`で使います宣言しないと反映されないので、1文加えましょう。

```C#
public Configuration()
{
    AutomaticMigrationsEnabled = true;
    SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
}
```

ここまで来たら準備完了。
`testtable`でデフォルト値の設定をしてやりましょう。

```C#
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Column(TypeName = "varchar")]
[StringLength(5)]
[DefaultValue(DefaultValue = "aaccc")]
public string varchartest { get; set; }

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue(DefaultValue = "4433")]
public int inttest { get; set; }
```

いざ`Add-Migration`！

![キャプチャ3.GIF](https://qiita-image-store.s3.amazonaws.com/0/147093/4c7596eb-c1b2-f78e-c2f1-17272c42abe8.gif)

からの`Update-Database`！

![キャプチャ4.GIF](https://qiita-image-store.s3.amazonaws.com/0/147093/3d632a57-0f93-8ea1-fb64-2430010ee496.gif)

んんん？
"aaccc"はだめ？どういうことでしょ。
`-Verbose`をつけてもう一度`Update-Database`してみます。

![キャプチャ5.GIF](https://qiita-image-store.s3.amazonaws.com/0/147093/f120974e-9343-888d-5fbb-719cdcab267d.gif)

DEFAULTのあとのaacccが文字列じゃない！
ひゃー、なるほど。ここが原因ですね。
とりあえずこう出力されるということは数値ならば大丈夫な感じしますねえ。
`aaccc`を`55555`と変更して`Update-Database`してみると、エラー無く終了。
制約の下にこんなのが作成されていました。

![キャプチャ6.GIF](https://qiita-image-store.s3.amazonaws.com/0/147093/9841547d-2d37-974c-f718-a79a60d12a8a.gif)

ほうほう。では適当にinsertして大丈夫か確認してみましょう。

![キャプチャ7.GIF](https://qiita-image-store.s3.amazonaws.com/0/147093/6dc45ea9-84c1-0609-9d16-bdfffdf251b9.gif)

問題なさそうですね。数値の初期値設定完了です。

書き始めた時は`Add-Migration`後に設定のDefaultValueに追加されているイメージだったんですが、`AnnotationValues`に追加されることでも可能だったんですね。


## 文字列の場合

じゃあ文字列のデフォルト値設定はどうするの、と。
いろいろ試したら、思いの外簡単にできました。

```C#
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Column(TypeName = "varchar")]
[StringLength(5)]
[DefaultValue(DefaultValue = "'aaccc'")]
public string varchartest { get; set; }
```

これでOKです。
`DefaultValue`に設定した文字列がそのままクエリに入るので、''から入れてやればこの通り。

![キャプチャ8.GIF](https://qiita-image-store.s3.amazonaws.com/0/147093/093e0b96-f9a0-290a-d7bc-080e0254567a.gif)

![キャプチャ9.GIF](https://qiita-image-store.s3.amazonaws.com/0/147093/bf14d344-6843-5a43-a13a-5cc4f1611ec6.gif)

うん、大丈夫そうです。

## 所感

実は昔これと同じことやろうとして断念したんですが、今回はできてよかったなあ。



一番大事な`SqlServerMigrationSqlGenerator`が抜けてたので追記しました。