Entity Framework 6コードの最初のデフォルト値


203

特定のプロパティにデフォルト値を与える「エレガントな」方法はありますか?

多分DataAnnotationsによって、次のようなもの:

[DefaultValue("true")]
public bool Active { get; set; }

ありがとうございました。


多分コンストラクタで試してみthis.Active = true;ませんか?私は、取得時にDB値が優先されると思うが、変更の追跡のように、最初にフェッチせずに更新のためにエンティティを取り付け、その後new'ing場合は慎重かもしれないあなたが値を更新したいと、これを参照してください。EFを長い間使っていなかったのでコメントします。これは暗闇の中でのショットのようです。
AaronLS 2013年

3
私はこれまでのところ、この方法を使用していた、応答をありがとうstackoverflow.com/a/5032578/2913441が、私は多分もっと良い方法があることを考えました。
marino-krk 2013年

2
public bool Inactive { get; set; }😉
ジェシーHufstetler

Microsoftのドキュメントによると、「データアノテーションを使用してデフォルト値を設定することはできません」。
hmfarimani 2018

回答:


166

最初にコードを手動で編集して移行できます。

public override void Up()
{    
   AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
} 

6
オブジェクトの作成時にOPが特別に設定されActiveていない場合、これが機能するかどうかはわかりません。デフォルト値は常にnull許容ではないboolプロパティにあるため、変更しない限り、これはエンティティフレームワークがデータベースに保存するものです。それとも何か不足していますか?trueEventfalse
GFoley83 2015

6
@ GFoley83、はい、あなたは正しいです。このメソッドは、データベースレベルでのみデフォルトの制約を追加します。完全な解決策として、エンティティのコンストラクタでデフォルト値を割り当てるか、上記の回答に示されているように
バッキング

1
これは基本型で機能します。DATETIMEOFFSETなどの場合、defaultValueSql: "SYSDATETIMEOFFSET"を使用します。defaultValueではなく、このdefaultValue:System.DateTimeOffset.Nowが現在のシステムのdatetimeoffset値の文字列に解決されます。
OzBob

3
私の知る限り、手動で行った変更は、移行を再足場する場合に失われます
Alexander Powolozki

1
@ninbit移行を記述してデータベースレベルで削除してから、DALマッピングを変更する必要があると思います
gdbdable

74

久しぶりですが、他の人にメモを残します。私は属性で必要なものを達成し、モデルクラスのフィールドを必要に応じてその属性で装飾しました。

[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }

次の2つの記事を参考にしてください。

私がしたこと:

属性を定義

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

コンテキストの「OnModelCreating」内

modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));

カスタムSqlGenerator

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

次に、移行構成コンストラクターで、カスタムSQLジェネレーターを登録します。

SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());

6
[SqlDefaultValue(DefaultValue = "getutcdate()")]すべてのエンティティを使用せずに、グローバルに実行することもできます。1)単に削除modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue)); 2)追加modelBuilder.Properties().Where(x => x.PropertyType == typeof(DateTime)).Configure(c => c.HasColumnType("datetime2").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue", "getdate()"));
DanielSkowroński

1
カスタムSqlGeneratorはどこにありますか?
ᴍᴀᴛᴛʙᴀᴋᴇʀ

3
カスタムSqlGeneratorはここから来た:andy.mehalick.com/2014/02/06/...
ravinsp

1
@ravinspなぜMigrationCodeGeneratorクラスをカスタマイズして、SqlGeneratorコードではなく正しい情報で移行するようにしないのですか?SQLコードは最後のステップです...
Alex

@アレックスワースしよう!これも機能し、SQLコードを挿入するよりもエレガントです。しかし、C#MigrationCodeGeneratorのオーバーライドの複雑さについてはよくわかりません。
ravinsp 2018年

73

上記の回答は本当に役に立ちましたが、ソリューションの一部しか提供しませんでした。主な問題は、デフォルト値属性を削除するとすぐに、データベースの列の制約が削除されないことです。そのため、以前のデフォルト値は引き続きデータベースに残ります。

これは、属性の削除に関するSQL制約の削除を含む、問題の完全な解決策です。.NET FrameworkのネイティブDefaultValue属性も再利用しています。

使用法

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }

これを機能させるには、IdentityModels.csファイルとConfiguration.csファイルを更新する必要があります

IdentityModels.csファイル

このメソッドをApplicationDbContextクラスに追加/更新します

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
            base.OnModelCreating(modelBuilder);
            var convention = new AttributeToColumnAnnotationConvention<DefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.SingleOrDefault().Value.ToString());
            modelBuilder.Conventions.Add(convention);
}

Configuration.csファイル

次のConfigurationように、カスタムSqlジェネレーターを登録して、クラスコンストラクターを更新します。

internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
    public Configuration()
    {
        // DefaultValue Sql Generator
        SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
    }
}

次に、カスタムSqlジェネレータークラスを追加します(Configuration.csファイルまたは別のファイルに追加できます)。

internal class DefaultValueSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    private int dropConstraintCount = 0;

    protected override void Generate(AddColumnOperation addColumnOperation)
    {
        SetAnnotatedColumn(addColumnOperation.Column, addColumnOperation.Table);
        base.Generate(addColumnOperation);
    }

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

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

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

    private void SetAnnotatedColumn(ColumnModel column, string tableName)
    {
        AnnotationValues values;
        if (column.Annotations.TryGetValue("SqlDefaultValue", out values))
        {
            if (values.NewValue == null)
            {
                column.DefaultValueSql = null;
                using (var writer = Writer())
                {
                    // Drop Constraint
                    writer.WriteLine(GetSqlDropConstraintQuery(tableName, column.Name));
                    Statement(writer);
                }
            }
            else
            {
                column.DefaultValueSql = (string)values.NewValue;
            }
        }
    }

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

    private string GetSqlDropConstraintQuery(string tableName, string columnName)
    {
        var tableNameSplittedByDot = tableName.Split('.');
        var tableSchema = tableNameSplittedByDot[0];
        var tablePureName = tableNameSplittedByDot[1];

        var str = $@"DECLARE @var{dropConstraintCount} nvarchar(128)
SELECT @var{dropConstraintCount} = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'{tableSchema}.[{tablePureName}]')
AND col_name(parent_object_id, parent_column_id) = '{columnName}';
IF @var{dropConstraintCount} IS NOT NULL
    EXECUTE('ALTER TABLE {tableSchema}.[{tablePureName}] DROP CONSTRAINT [' + @var{dropConstraintCount} + ']')";

        dropConstraintCount = dropConstraintCount + 1;
        return str;
    }
}

2
このアプローチは私にとっては完璧に機能しました。私が行った1つの拡張は、Generate(CreateTableOperation createTableOperation)およびGenerate(AddColumnOperation addColumnOperation)も同じロジックでオーバーライドして、これらのシナリオもキャッチされるようにすることでした。また、値のみをチェックします。デフォルトを空の文字列にしたかったため、NewValueはnullです。
デロリアン

2
@Delorian私は私の答えを更新しました。コメントをありがとう
Jevgenij Martynenko 2016

1
ロールバックが複数の制約をドロップしているインスタンスをサポートするために、あなたの投稿を編集しました。スクリプトは、@ conがすでに宣言されていることを示すエラーをスローします。カウンターを保持して単純にインクリメントするプライベート変数を作成しました。また、ドロップ制約のフォーマットを変更して、制約の作成時にEFがSQLに送信するものとより厳密に一致させました。これで素晴らしい仕事です!
ブラッド

1
解決策に感謝しますが、2つの問題があります。1.テーブル名にはブラケットが必要です。2.アップデートでは、新しい値は設定されず、代わりにデフォルト値が設定されます!
Omid-RH

1
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]属性を設定する必要がありますか?もしそうなら、なぜですか?私のテストでは、それを省略しても効果がないように見えました。
RamNow 2018

26

モデルプロパティは「自動プロパティ」である必要はありません。そして、DefaultValue属性は実際には情報を提供するメタデータのみです。ここで受け入れられる答えは、コンストラクターアプローチの1つの代替手段です。

public class Track
{

    private const int DEFAULT_LENGTH = 400;
    private int _length = DEFAULT_LENGTH;
    [DefaultValue(DEFAULT_LENGTH)]
    public int LengthInMeters {
        get { return _length; }
        set { _length = value; }
    }
}

public class Track
{
    public Track()
    {
        LengthInMeters = 400;   
    }

    public int LengthInMeters { get; set; }        
}

これは、この特定のクラスを使用してデータを作成および消費するアプリケーションでのみ機能します。データアクセスコードが集中化されている場合、通常これは問題になりません。すべてのアプリケーションで値を更新するには、データソースを構成してデフォルト値を設定する必要があります。Deviの答えは、移行、SQL、またはデータソースが話す言語を使用してそれを行う方法を示しています。


21
注:これにより、データベースにデフォルト値設定されません。エンティティを使用しない他のプログラムは、そのデフォルト値を取得しません。
エリックJ.

答えのこの部分ですが、Entity Framework以外の方法でレコードを挿入する場合は機能しません。また、テーブルにnull以外の新しい列を作成する場合、既存のレコードのデフォルト値を設定することはできません。@deviには以下の貴重な追加があります。
d512 2015

なぜあなたの最初のアプローチは「正しい」方法ですか?コンストラクターアプローチで予期しない問題に遭遇しますか?
WiteCastle 2015年

意見の問題、そしてそれらの変更:)そしておそらくそうではありません。
calebboyd 2015年

2
特定の状況下で、コンストラクターのアプローチに問題がありました。バッキングフィールドにデフォルト値を設定することは、最も侵襲性の低いソリューションのようです。
Lucent Fox

11

私がしたことは、エンティティのコンストラクタで値を初期化しました

注:DefaultValue属性はプロパティの値を自動的に設定しないため、自分で設定する必要があります


4
値を設定するコンストラクターの問題は、EFがトランザクションをコミットするときにデータベースを更新することです。
ルカ

モデルは最初にこの方法でデフォルト値を作成します
Lucas

DefaultValueは、遠く離れた外国に住んでいる種であり、自分の所有物だけでは遭遇するのが恥ずかしいです。音を出さないでください。簡単に怖がってしまいます-聞こえるほど十分に近づいたとしても。まったく明白でないことを述べるための+1。
Risadinha

8

@SedatKapanogluコメントの後、彼が正しかったので、機能するすべての私のアプローチを追加しています。流れるようなAPIを使用するだけでは機能しません。

1-カスタムコードジェネレーターを作成し、ColumnModelのGenerateをオーバーライドします。

   public class ExtendedMigrationCodeGenerator : CSharpMigrationCodeGenerator
{

    protected override void Generate(ColumnModel column, IndentedTextWriter writer, bool emitName = false)
    {

        if (column.Annotations.Keys.Contains("Default"))
        {
            var value = Convert.ChangeType(column.Annotations["Default"].NewValue, column.ClrDefaultValue.GetType());
            column.DefaultValue = value;
        }


        base.Generate(column, writer, emitName);
    }

}

2-新しいコードジェネレーターを割り当てます。

public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
    public Configuration()
    {
        CodeGenerator = new ExtendedMigrationCodeGenerator();
        AutomaticMigrationsEnabled = false;
    }
}

3- Fluent APIを使用してアノテーションを作成します。

public static void Configure(DbModelBuilder builder){    
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);            
}

私の完全なソリューションを見ることができます、私はそれがどのように機能するかの私の実装をすべて追加しました、ありがとう。
デニープイグ2017

5

それは簡単です!必要に応じて注釈を付けるだけです。

[Required]
public bool MyField { get; set; }

その結果、移行は次のようになります。

migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);

trueが必要な場合は、データベースを更新する前に、マイグレーションでdefaultValueをtrueに変更します


その後、自動生成された移行を変更して、デフォルト値を推測することができます
Fernando Torres

シンプルで機能し、新しい列に外部キー制約がある既存のテーブルに列をすばやく追加するのに役立ちます。ありがとう
po10cySA

そして、デフォルト値を必要とする場合はどうtrueでしょうか?
Christos Lytras

5

私のアプローチは「コードファースト」の概念全体を回避していることを認めます。しかし、テーブル自体のデフォルト値を変更するだけの機能がある場合は、上記の手順を実行する必要がある長さよりもはるかに簡単です...私はすべての作業を行うのが面倒です!

まるでポスターのオリジナルのアイデアが機能するかのようです。

[DefaultValue(true)]
public bool IsAdmin { get; set; }

私は彼らが引用符を追加するのは間違いだと思っていました...しかし、残念ながらそのような直感はありません。他の提案は私にとっては大きすぎました(テーブルに移動して変更を加えるために必要な特権を持っていることは確かですが、あらゆる状況ですべての開発者がそうするわけではありません)。結局私はそれを昔ながらのやり方でやった。SQL Serverテーブルにデフォルト値を設定しました...本当に、もう十分です!注:移行の追加とデータベースの更新を実行し、変更がスタックしたことをさらにテストしました。 ここに画像の説明を入力してください


1

Modelクラスのデフォルトコンストラクターをオーバーロードし、使用してもしなくてもよい関連パラメーターを渡します。これにより、属性のデフォルト値を簡単に指定できます。以下に例を示します。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Aim.Data.Domain
{
    [MetadataType(typeof(LoginModel))]
    public partial class Login
    {       
        public Login(bool status)
        {
            this.CreatedDate = DateTime.Now;
            this.ModifiedDate = DateTime.Now;
            this.Culture = "EN-US";
            this.IsDefaultPassword = status;
            this.IsActive = status;
            this.LoginLogs = new HashSet<LoginLog>();
            this.LoginLogHistories = new HashSet<LoginLogHistory>();
        }


    }

    public class LoginModel
    {

        [Key]
        [ScaffoldColumn(false)] 
        public int Id { get; set; }
        [Required]
        public string LoginCode { get; set; }
        [Required]
        public string Password { get; set; }
        public string LastPassword { get; set; }     
        public int UserGroupId { get; set; }
        public int FalseAttempt { get; set; }
        public bool IsLocked { get; set; }
        public int CreatedBy { get; set; }       
        public System.DateTime CreatedDate { get; set; }
        public Nullable<int> ModifiedBy { get; set; }      
        public Nullable<System.DateTime> ModifiedDate { get; set; }       
        public string Culture { get; set; }        
        public virtual ICollection<LoginLog> LoginLogs { get; set; }
        public virtual ICollection<LoginLogHistory> LoginLogHistories { get; set; }
    }

}

この提案は完全にクライアント側のロジックです。これは、アプリケーションを使用してデータベースとのみ対話する限り、「機能します」。誰かが手動で、または別のアプリケーションからレコードを挿入しようとするとすぐに、スキーマにデフォルトの式がなく、これが他のクライアントにスケーリングできないという欠点に気付きます。明示的には述べられていませんが、この正当なトピックは、デフォルトの式を列定義に配置する移行をEFに作成させる要件を意味します。
トム・

0

Productsという名前のクラス名があり、IsActiveフィールドがあるとします。あなただけの作成コンストラクタが必要です:

Public class Products
{
    public Products()
    {
       IsActive = true;
    }
 public string Field1 { get; set; }
 public string Field2 { get; set; }
 public bool IsActive { get; set; }
}

次に、IsActiveのデフォルト値はTrueです!

Edite

SQLでこれを行う場合は、次のコマンドを使用します。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.IsActive)
        .HasDefaultValueSql("true");
}

単にこれは問題ではありませんでした。これは、データベース自体のデフォルト値の制約に関するものでした。
ガボール

4
@Hatef、あなたの編集はEF Coreにのみ適用されます。質問はEF 6に関するものです
マイケル

3
このHasDefaultValueSqlはEF6では利用できません
tatigo

0

2016年6月27日にリリースされたEFコアでは、流暢なAPIを使用してデフォルト値を設定できます。ApplicationDbContextクラスに移動し、メソッド名OnModelCreatingを見つけて作成し、次のFluent APIを追加します。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourTableName>()
        .Property(b => b.Active)
        .HasDefaultValue(true);
}

0

.NET Core 3.1では、モデルクラスで次のことができます。

    public bool? Active { get; set; } 

DbContext OnModelCreatingにデフォルト値を追加します。

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Foundation>()
            .Property(b => b.Active)
            .HasDefaultValueSql("1");

        base.OnModelCreating(modelBuilder);
    }

データベースで次の結果になります

ここに画像の説明を入力してください

注:プロパティにnull許容(bool?)がない場合、次の警告が表示されます

The 'bool' property 'Active' on entity type 'Foundation' is configured with a database-generated default. This default will always be used for inserts when the property has the value 'false', since this is the CLR default for the 'bool' type. Consider using the nullable 'bool?' type instead so that the default will only be used for inserts when the property value is 'null'.

-3

エンティティプロパティでAuto-Property Initializerを使用するだけで十分です。

例えば:

public class Thing {
    public bool IsBigThing{ get; set; } = false;
}

2
あなたはこれが最初にコードでうまくいくと思います。しかし、EF 6.2では、それは私にはありませんでした。
user1040323 2018年

-4

うーん...私は最初にDBを実行します。その場合、これは実際にははるかに簡単です。EF6でしょ?モデルを開き、デフォルトを設定する列を右クリックしてプロパティを選択するだけで、「DefaultValue」フィールドが表示されます。記入して保存してください。それはあなたのためにコードを設定します。

あなたのマイレージは最初にコードによって異なるかもしれません、私はそれで働いていません。

他の多くのソリューションの問題は、最初は機能するかもしれませんが、モデルを再構築するとすぐに、マシンが生成したファイルに挿入したカスタムコードがスローされることです。

このメソッドは、edmxファイルにプロパティを追加することで機能します。

<EntityType Name="Thingy">
  <Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />

そして、必要なコードをコンストラクターに追加することによって:

public Thingy()
{
  this.Iteration = 1;

-5

MSSQL Serverのテーブルの列のデフォルト値を設定し、クラスコードで次のように属性を追加します。

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

同じプロパティ。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.