特定のプロパティにデフォルト値を与えるための「エレガントな」方法はありますか?
DataAnnotationsによると、
[DefaultValue("true")]
public bool Active { get; set; }
ありがとうございました。
最初の移行を手動で編集することでそれを行うことができます。
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
それはしばらく経ちました、しかし他の人のためにメモを残します。私はある属性で必要とされることを達成し、そして私は自分のモデルクラスフィールドをその属性で望むように装飾した。
[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;
}
}
次に、Migration Configurationコンストラクターで、カスタムSQLジェネレーターを登録します。
SetSqlGenerator("System.Data.SqlClient", new HarmonyMigrationSqlGenerator());
上記の回答は本当に役に立ちましたが、解決策の一部しか得られませんでした。主な問題は、Default value属性を削除してもデータベース内の列に対する制約が削除されないことです。そのため、以前のデフォルト値はデータベースに残ります。
これは、属性削除に対する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);
}
次のようにカスタムSqlジェネレータを登録してConfiguration
クラスコンストラクタを更新します。
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;
}
}
モデルのプロパティは、自動プロパティである必要はありません。そしてDefaultValue属性は実際には有益なメタデータにすぎません。ここで受け入れられている という答え は、コンストラクタアプローチの代替案です。
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; }
}
}
vs.
public class Track
{
public Track()
{
LengthInMeters = 400;
}
public int LengthInMeters { get; set; }
}
これは、この特定のクラスを使用してデータを作成および消費するアプリケーションに対してのみ機能します。通常、データアクセスコードが集中管理されていれば、これは問題になりません。 all アプリケーション間で値を更新するには、デフォルト値を設定するようにデータソースを設定する必要があります。 Deviの答え は、マイグレーション、SQL、あるいはあなたのデータソースが話すどんな言語を使ってもそれができる方法を示しています。
私がしたこと、エンティティのコンストラクタで値を初期化した
注意:DefaultValue属性はあなたのプロパティの値を自動的には設定しません、あなたはあなた自身がそれをしなければなりません
@SedatKapanogluのコメントの後、私はうまくいく私のアプローチをすべて追加しています。
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-注釈を作成するために流暢なapiを使用します。
public static void Configure(DbModelBuilder builder){
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);
}
私のアプローチは「コードファースト」のアプローチ全体から逃れることを認めます。しかし、テーブル自体のデフォルト値を変更するだけの能力があるのであれば、上記の長さよりもずっと簡単です。
ポスターオリジナルのアイデアがうまくいくかのようにそれはほとんど思われます:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
私は彼らがただ引用符を追加することの間違いをしたと思った…しかし残念ながらそのような直観的なことはありません。他の提案は私には多すぎる(私がテーブルに入って変更を加えるのに必要な特権を持っていることを許可されています...すべての状況で開発者がそんなにしないところ)最後に、私はそれを昔ながらのやり方でやっただけです。 SQL Serverテーブルにデフォルト値を設定しました。 注:私はさらにadd-migrationとupdate-databaseをすることをテストし、変更は行き詰まった。
それは簡単です!単にrequiredと注釈を付けます。
[Required]
public bool MyField { get; set; }
その結果、移行は次のようになります。
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
Trueにしたい場合は、データベースを更新する前に、マイグレーションでdefaultValueをtrueに変更してください。
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; }
}
}