特定のプロパティにデフォルト値を与える「エレガントな」方法はありますか?
多分DataAnnotationsによって、次のようなもの:
[DefaultValue("true")]
public bool Active { get; set; }
ありがとうございました。
public bool Inactive { get; set; }
😉
特定のプロパティにデフォルト値を与える「エレガントな」方法はありますか?
多分DataAnnotationsによって、次のようなもの:
[DefaultValue("true")]
public bool Active { get; set; }
ありがとうございました。
public bool Inactive { get; set; }
😉
回答:
最初にコードを手動で編集して移行できます。
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
Active
ていない場合、これが機能するかどうかはわかりません。デフォルト値は常にnull許容ではないboolプロパティにあるため、変更しない限り、これはエンティティフレームワークがデータベースに保存するものです。それとも何か不足していますか?true
Event
false
久しぶりですが、他の人にメモを残します。私は属性で必要なものを達成し、モデルクラスのフィールドを必要に応じてその属性で装飾しました。
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
次の2つの記事を参考にしてください。
私がしたこと:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
public string DefaultValue { get; set; }
}
modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
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());
[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()"));
上記の回答は本当に役に立ちましたが、ソリューションの一部しか提供しませんでした。主な問題は、デフォルト値属性を削除するとすぐに、データベースの列の制約が削除されないことです。そのため、以前のデフォルト値は引き続きデータベースに残ります。
これは、属性の削除に関するSQL制約の削除を含む、問題の完全な解決策です。.NET FrameworkのネイティブDefaultValue
属性も再利用しています。
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }
これを機能させるには、IdentityModels.csファイルとConfiguration.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
ように、カスタム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;
}
}
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
属性を設定する必要がありますか?もしそうなら、なぜですか?私のテストでは、それを省略しても効果がないように見えました。
モデルプロパティは「自動プロパティ」である必要はありません。そして、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、またはデータソースが話す言語を使用してそれを行う方法を示しています。
私がしたことは、エンティティのコンストラクタで値を初期化しました
注:DefaultValue属性はプロパティの値を自動的に設定しないため、自分で設定する必要があります
@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);
}
それは簡単です!必要に応じて注釈を付けるだけです。
[Required]
public bool MyField { get; set; }
その結果、移行は次のようになります。
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
trueが必要な場合は、データベースを更新する前に、マイグレーションでdefaultValueをtrueに変更します
true
でしょうか?
私のアプローチは「コードファースト」の概念全体を回避していることを認めます。しかし、テーブル自体のデフォルト値を変更するだけの機能がある場合は、上記の手順を実行する必要がある長さよりもはるかに簡単です...私はすべての作業を行うのが面倒です!
まるでポスターのオリジナルのアイデアが機能するかのようです。
[DefaultValue(true)]
public bool IsAdmin { get; set; }
私は彼らが引用符を追加するのは間違いだと思っていました...しかし、残念ながらそのような直感はありません。他の提案は私にとっては大きすぎました(テーブルに移動して変更を加えるために必要な特権を持っていることは確かですが、あらゆる状況ですべての開発者がそうするわけではありません)。結局私はそれを昔ながらのやり方でやった。SQL Serverテーブルにデフォルト値を設定しました...本当に、もう十分です!注:移行の追加とデータベースの更新を実行し、変更がスタックしたことをさらにテストしました。
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; }
}
}
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");
}
.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'.
エンティティプロパティでAuto-Property Initializerを使用するだけで十分です。
例えば:
public class Thing {
public bool IsBigThing{ get; set; } = false;
}
うーん...私は最初にDBを実行します。その場合、これは実際にははるかに簡単です。EF6でしょ?モデルを開き、デフォルトを設定する列を右クリックしてプロパティを選択するだけで、「DefaultValue」フィールドが表示されます。記入して保存してください。それはあなたのためにコードを設定します。
あなたのマイレージは最初にコードによって異なるかもしれません、私はそれで働いていません。
他の多くのソリューションの問題は、最初は機能するかもしれませんが、モデルを再構築するとすぐに、マシンが生成したファイルに挿入したカスタムコードがスローされることです。
このメソッドは、edmxファイルにプロパティを追加することで機能します。
<EntityType Name="Thingy">
<Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />
そして、必要なコードをコンストラクターに追加することによって:
public Thingy()
{
this.Iteration = 1;
MSSQL Serverのテーブルの列のデフォルト値を設定し、クラスコードで次のように属性を追加します。
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
同じプロパティ。
this.Active = true;
ませんか?私は、取得時にDB値が優先されると思うが、変更の追跡のように、最初にフェッチせずに更新のためにエンティティを取り付け、その後new'ing場合は慎重かもしれないあなたが値を更新したいと、これを参照してください。EFを長い間使っていなかったのでコメントします。これは暗闇の中でのショットのようです。