Add-Migration前に初期値設定ができない
カラムにデフォルト値設定なんてよくあること。
しかしながらオプションにはそれららしいものが出てこない。
資料探して読んでみるもよくわからず。
なんでdefaultvalueが存在しているのに設定できないの???おかしくない???
設定にあるんだからできるはず。やってみましょう。
デフォルト値設定
http://stackoverflow.com/questions/19554050/entity-framework-6-code-first-default-value
defaultvalueがAdd-Migration前に設定できないのなんて先人が話題にしてないはずもなく。
雰囲気としては前回と変わらなそうな感じがしますね。
規約作る→modelBuilder.Conventions
にAddの流れで問題なさそうです。
ではまず規約を作りましょう。
今回はHasColumnAnnotation
に設定します。
[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すると。
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を作ります。
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文加えましょう。
public Configuration()
{
AutomaticMigrationsEnabled = true;
SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
}
ここまで来たら準備完了。
testtable
でデフォルト値の設定をしてやりましょう。
[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
!
からのUpdate-Database
!
んんん?
"aaccc"はだめ?どういうことでしょ。
-Verbose
をつけてもう一度Update-Database
してみます。
DEFAULTのあとのaacccが文字列じゃない!
ひゃー、なるほど。ここが原因ですね。
とりあえずこう出力されるということは数値ならば大丈夫な感じしますねえ。
aaccc
を55555
と変更してUpdate-Database
してみると、エラー無く終了。
制約の下にこんなのが作成されていました。
ほうほう。では適当にinsertして大丈夫か確認してみましょう。
問題なさそうですね。数値の初期値設定完了です。
書き始めた時はAdd-Migration
後に設定のDefaultValueに追加されているイメージだったんですが、AnnotationValues
に追加されることでも可能だったんですね。
文字列の場合
じゃあ文字列のデフォルト値設定はどうするの、と。
いろいろ試したら、思いの外簡単にできました。
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Column(TypeName = "varchar")]
[StringLength(5)]
[DefaultValue(DefaultValue = "'aaccc'")]
public string varchartest { get; set; }
これでOKです。
DefaultValue
に設定した文字列がそのままクエリに入るので、''から入れてやればこの通り。
うん、大丈夫そうです。
所感
実は昔これと同じことやろうとして断念したんですが、今回はできてよかったなあ。
一番大事なSqlServerMigrationSqlGenerator
が抜けてたので追記しました。