จะมีวิธี "ฉลาด" เพื่อให้ค่าเริ่มต้นคุณสมบัติเฉพาะ?
บางทีโดย 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
เพื่อให้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;
}
}
จากนั้นในคอนสตรัคเตอร์ Migration Configuration ให้ลงทะเบียนตัวสร้าง 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 ในการลบแอตทริบิวต์ ฉันกำลังใช้DefaultValue
แอตทริบิวต์ดั้งเดิมของ. NET Framework อีกครั้ง
[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 เป็นเพียงข้อมูลเมตาดาต้าที่ได้รับคำตอบที่นี่เป็นอีกทางเลือกหนึ่งสำหรับวิธีการสร้าง
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 หรือภาษาใดก็ตามที่แหล่งข้อมูลของคุณพูด
สิ่งที่ฉันทำฉันเริ่มต้นค่าในตัวสร้างของเอนทิตี
หมายเหตุ: แอตทริบิวต์ค่าเริ่มต้นจะไม่ตั้งค่าคุณสมบัติของคุณโดยอัตโนมัติคุณต้องทำด้วยตัวเอง
หลังจากความคิดเห็น @SedatKapanoglu ฉันกำลังเพิ่มวิธีการทั้งหมดที่ใช้งานได้เพราะเขาพูดถูกเพียงใช้ API ได้อย่างคล่องแคล่วไม่ทำงาน
1- สร้างตัวสร้างโค้ดที่กำหนดเองและแทนที่สร้างสำหรับ 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- กำหนดตัวสร้างรหัสใหม่:
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);
}
มันง่ายมาก! เพียงแค่ใส่คำอธิบายประกอบที่จำเป็น
[Required]
public bool MyField { get; set; }
ผลลัพธ์การโยกย้ายจะเป็น:
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
ถ้าคุณต้องการจริงเปลี่ยนค่าเริ่มต้นเป็นจริงในการโยกย้ายก่อนที่จะปรับปรุงฐานข้อมูล
true
ล่ะ?
ฉันยอมรับว่าวิธีการของฉันหนีจากแนวคิด "Code First" ทั้งหมด แต่ถ้าคุณมีความสามารถในการเปลี่ยนค่าเริ่มต้นในตารางตัวเอง ... มันง่ายกว่าความยาวที่คุณต้องทำตามข้างต้น ... ฉันขี้เกียจเกินกว่าที่จะทำงานทั้งหมดได้!
ดูเหมือนว่าแนวคิดดั้งเดิมของผู้โพสต์จะใช้งานได้:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
ฉันคิดว่าพวกเขาทำผิดพลาดในการเพิ่มคำพูด ... แต่ก็ไม่ได้หยั่งรู้เช่นนั้น คำแนะนำอื่น ๆ มีมากเกินไปสำหรับฉัน (อนุญาตให้ฉันมีสิทธิ์ที่จำเป็นในการเข้าไปในตารางและทำการเปลี่ยนแปลง ... ซึ่งนักพัฒนาไม่ได้ทุกคนจะได้ในทุกสถานการณ์) ในที่สุดฉันก็ทำแบบเก่า ๆ ฉันตั้งค่าเริ่มต้นในตาราง SQL Server ... ฉันหมายถึงเพียงพอแล้ว! หมายเหตุ: ฉันทดสอบเพิ่มเติมเกี่ยวกับการเพิ่มการย้ายข้อมูลและการอัปเดตฐานข้อมูลและการเปลี่ยนแปลงที่ติดอยู่
Just Overload ตัวสร้างปริยายของคลาส 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; }
}
}
ให้พิจารณาว่าคุณมีชื่อคลาสที่ชื่อผลิตภัณฑ์และคุณมีฟิลด์ IsActive เพียงคุณต้องสร้างคอนสตรัค:
Public class Products
{
public Products()
{
IsActive = true;
}
public string Field1 { get; set; }
public string Field2 { get; set; }
public bool IsActive { get; set; }
}
จากนั้นค่าเริ่มต้น IsActive ของคุณจะเป็นจริง!
Edite :
ถ้าคุณต้องการทำสิ่งนี้กับ SQL ให้ใช้คำสั่งนี้:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.IsActive)
.HasDefaultValueSql("true");
}
ใน EF core ที่วางจำหน่าย 27 มิถุนายน 2016 คุณสามารถใช้ API ได้อย่างคล่องแคล่วสำหรับการตั้งค่าเริ่มต้น ไปที่คลาส ApplicationDbContext ค้นหา / สร้างชื่อเมธอด OnModelCreating และเพิ่ม API ได้อย่างคล่องแคล่วต่อไปนี้
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourTableName>()
.Property(b => b.Active)
.HasDefaultValue(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'.
ฉันพบว่าเพียงแค่ใช้คุณสมบัติเริ่มต้นอัตโนมัติกับคุณสมบัติเอนทิตีก็เพียงพอที่จะทำงานให้เสร็จได้
ตัวอย่างเช่น:
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 และในแอตทริบิวต์รหัสเพิ่มระดับเช่นนี้:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
สำหรับคุณสมบัติเดียวกัน
this.Active = true;
? ฉันคิดว่าค่า DB จะมีความสำคัญเมื่อดึงข้อมูล แต่ระวังถ้ามีสิ่งใหม่แล้วแนบเอนทิตีสำหรับการอัปเดตโดยไม่ดึงข้อมูลมาก่อนเนื่องจากการติดตามการเปลี่ยนแปลงอาจเห็นสิ่งนี้ตามที่คุณต้องการอัปเดตค่า ความคิดเห็นเพราะฉันไม่ได้ใช้ EF มานานและฉันรู้สึกว่านี่เป็นการถ่ายภาพในที่มืด