Adakah cara "elegan" untuk memberikan properti default nilai default?
Mungkin oleh DataAnnotations, sesuatu seperti:
[DefaultValue("true")]
public bool Active { get; set; }
Terima kasih.
public bool Inactive { get; set; }
😉
Adakah cara "elegan" untuk memberikan properti default nilai default?
Mungkin oleh DataAnnotations, sesuatu seperti:
[DefaultValue("true")]
public bool Active { get; set; }
Terima kasih.
public bool Inactive { get; set; }
😉
Jawaban:
Anda dapat melakukannya dengan mengedit kode migrasi terlebih dahulu secara manual:
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
Active
untuk true
saat membuat Event
objek juga. Nilai default selalu berada false
pada properti bool yang tidak dapat dibatalkan, jadi kecuali diubah, ini adalah kerangka kerja entitas yang akan disimpan ke db. Atau apakah saya melewatkan sesuatu?
Sudah lama, tetapi meninggalkan catatan untuk orang lain. Saya mencapai apa yang dibutuhkan dengan atribut dan saya menghiasi bidang kelas model saya dengan atribut yang saya inginkan.
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
Mendapat bantuan dari 2 artikel ini:
Apa yang saya lakukan:
[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;
}
}
Kemudian di konstruktor Konfigurasi Migrasi, daftarkan generator SQL khusus.
SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());
[SqlDefaultValue(DefaultValue = "getutcdate()")]
setiap entitas. 1) Cukup hapus modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
2) TambahmodelBuilder.Properties().Where(x => x.PropertyType == typeof(DateTime)).Configure(c => c.HasColumnType("datetime2").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue", "getdate()"));
Jawaban di atas sangat membantu, tetapi hanya memberikan sebagian dari solusi. Masalah utama adalah bahwa segera setelah Anda menghapus atribut nilai Default, batasan pada kolom dalam database tidak akan dihapus. Jadi nilai default sebelumnya akan tetap berada di database.
Berikut ini adalah solusi lengkap untuk masalah ini, termasuk penghapusan kendala SQL pada penghapusan atribut. Saya juga menggunakan kembali DefaultValue
atribut asli .NET Framework .
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }
Agar ini berfungsi, Anda perlu memperbarui file IdentityModels.cs dan Configuration.cs
Tambahkan / perbarui metode ini di ApplicationDbContext
kelas Anda
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);
}
Perbarui Configuration
konstruktor kelas Anda dengan mendaftarkan generator Sql khusus, seperti ini:
internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
public Configuration()
{
// DefaultValue Sql Generator
SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
}
}
Selanjutnya, tambahkan kelas generator Sql khusus (Anda dapat menambahkannya ke file Configuration.cs atau file terpisah)
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)]
atribut? Jika demikian, mengapa? Dalam tes saya sepertinya tidak berpengaruh meninggalkannya.
Properti model Anda tidak harus menjadi 'properti otomatis' Meskipun itu lebih mudah. Dan atribut DefaultValue benar-benar hanya metadata informatif. Jawaban yang diterima di sini adalah salah satu alternatif dari pendekatan konstruktor.
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; }
}
Ini hanya akan berfungsi untuk aplikasi yang membuat dan menggunakan data menggunakan kelas khusus ini. Biasanya ini bukan masalah jika kode akses data terpusat. Untuk memperbarui nilai di semua aplikasi, Anda perlu mengkonfigurasi sumber data untuk menetapkan nilai default. Jawaban Devi menunjukkan bagaimana hal itu dapat dilakukan dengan menggunakan migrasi, sql, atau bahasa apa pun yang digunakan oleh sumber data Anda.
Apa yang saya lakukan, saya menginisialisasi nilai dalam konstruktor entitas
Catatan: Atribut DefaultValue tidak akan menetapkan nilai properti Anda secara otomatis, Anda harus melakukannya sendiri
Setelah komentar @SedatKapanoglu, saya menambahkan semua pendekatan saya yang berfungsi, karena dia benar, hanya menggunakan API yang lancar tidak bekerja.
1- Buat pembuat kode khusus dan ganti Generate untuk ColumnModel.
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- Tetapkan pembuat kode baru:
public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
public Configuration()
{
CodeGenerator = new ExtendedMigrationCodeGenerator();
AutomaticMigrationsEnabled = false;
}
}
3- Gunakan api lancar untuk membuat Anotasi:
public static void Configure(DbModelBuilder builder){
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);
}
Itu mudah! Hanya perlu penjelasan.
[Required]
public bool MyField { get; set; }
migrasi yang dihasilkan adalah:
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
Jika Anda ingin true, ubah DefaultValue menjadi true dalam migrasi sebelum memperbarui database
true
?
Saya akui bahwa pendekatan saya lolos dari keseluruhan konsep "Code First". Tetapi jika Anda memiliki kemampuan untuk hanya mengubah nilai default di tabel itu sendiri ... itu jauh lebih sederhana daripada panjang yang harus Anda lalui di atas ... Saya terlalu malas untuk melakukan semua itu bekerja!
Tampaknya seolah-olah ide-ide asli poster akan berhasil:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
Saya pikir mereka hanya membuat kesalahan dengan menambahkan tanda kutip ... tapi sayangnya tidak ada intuisi seperti itu. Saran lain terlalu banyak bagi saya (mengingat saya memiliki hak istimewa yang diperlukan untuk masuk ke meja dan membuat perubahan ... di mana tidak setiap pengembang akan dalam setiap situasi). Pada akhirnya saya hanya melakukannya dengan cara kuno. Saya menetapkan nilai default dalam tabel SQL Server ... Maksud saya benar-benar, sudah cukup! CATATAN: Saya selanjutnya diuji melakukan add-migrasi dan pembaruan-database dan perubahan macet.
Hanya Membebani konstruktor default kelas Model dan melewati parameter terkait yang mungkin Anda gunakan atau tidak. Dengan ini, Anda dapat dengan mudah memberikan nilai default untuk atribut. Di bawah ini adalah contohnya.
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; }
}
}
Mari kita anggap Anda memiliki nama kelas bernama Produk dan Anda memiliki bidang IsActive. Anda hanya perlu membuat konstruktor:
Public class Products
{
public Products()
{
IsActive = true;
}
public string Field1 { get; set; }
public string Field2 { get; set; }
public bool IsActive { get; set; }
}
Maka nilai default IsActive Anda adalah Benar!
Edite :
jika Anda ingin melakukan ini dengan SQL, gunakan perintah ini:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.IsActive)
.HasDefaultValueSql("true");
}
Di EF core yang dirilis 27 Juni 2016 Anda dapat menggunakan API yang lancar untuk menetapkan nilai default. Pergi ke kelas ApplicationDbContext, cari / buat nama metode OnModelCreating dan tambahkan API lancar berikut.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourTableName>()
.Property(b => b.Active)
.HasDefaultValue(true);
}
Di .NET Core 3.1 Anda dapat melakukan hal berikut di kelas model:
public bool? Active { get; set; }
Di DbContext OnModelCreating Anda menambahkan nilai default.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Foundation>()
.Property(b => b.Active)
.HasDefaultValueSql("1");
base.OnModelCreating(modelBuilder);
}
Menghasilkan berikut dalam database
Catatan: Jika Anda tidak memiliki properti nullable (bool?) Untuk Anda, Anda akan mendapatkan peringatan berikut
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'.
Saya menemukan bahwa hanya menggunakan Auto-Property Initializer pada properti entitas sudah cukup untuk menyelesaikan pekerjaan.
Sebagai contoh:
public class Thing {
public bool IsBigThing{ get; set; } = false;
}
Hmm ... Saya melakukan DB terlebih dahulu, dan dalam hal ini, ini sebenarnya jauh lebih mudah. EF6 bukan? Cukup buka model Anda, klik kanan pada kolom yang ingin Anda tetapkan default, pilih properti, dan Anda akan melihat bidang "DefaultValue". Cukup isi dan simpan. Ini akan mengatur kode untuk Anda.
Jarak tempuh Anda mungkin bervariasi pada kode terlebih dahulu, saya belum bekerja dengan itu.
Masalah dengan banyak solusi lain, adalah bahwa meskipun mereka dapat bekerja pada awalnya, segera setelah Anda membangun kembali model, itu akan membuang kode kustom apa pun yang Anda masukkan ke file yang dihasilkan mesin.
Metode ini berfungsi dengan menambahkan properti tambahan ke file edmx:
<EntityType Name="Thingy">
<Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />
Dan dengan menambahkan kode yang diperlukan ke konstruktor:
public Thingy()
{
this.Iteration = 1;
Tetapkan nilai default untuk kolom dalam tabel di MSSQL Server, dan dalam atribut class code add, seperti ini:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
untuk properti yang sama.
this.Active = true;
? Saya pikir nilai DB akan diutamakan saat mengambil, tapi hati-hati jika baru kemudian lampirkan entitas untuk pembaruan tanpa mengambil pertama, karena pelacakan perubahan mungkin melihat ini ketika Anda ingin memperbarui nilai. Komentar karena saya belum pernah menggunakan EF dalam waktu yang lama, dan saya merasa ini adalah kesempatan yang sulit.