ค่าดีฟอลต์สำหรับฟิลด์ที่ต้องการในการโอนย้าย Entity Framework?


92

ฉันได้เพิ่ม[Required]บันทึกย่อข้อมูลให้เป็นหนึ่งในรูปแบบของฉันในโปรแกรมประยุกต์ ASP.NET MVC หลังจากสร้างการโอนย้ายการรันUpdate-Databaseคำสั่งจะทำให้เกิดข้อผิดพลาดต่อไปนี้:

ไม่สามารถแทรกค่า NULL ลงในคอลัมน์ 'Director', table 'MOVIES_cf7bad808fa94f89afa2e5dae1161e78.dbo.Movies'; คอลัมน์ไม่อนุญาตให้มีค่าว่าง UPDATE ล้มเหลว คำสั่งยุติแล้ว

เนื่องจากบางระเบียนมีค่า NULL ในDirectorคอลัมน์ ฉันจะเปลี่ยนค่าเหล่านั้นให้เป็นค่าเริ่มต้น (พูดว่า "John Doe") โดยอัตโนมัติได้อย่างไร

นี่คือโมเดลของฉัน:

  public class Movie
    {
        public int ID { get; set; }
        [Required]
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }

        [Required]
        public string Genre { get; set; }

        [Range(1,100)]
        [DataType(DataType.Currency)]
        public decimal Price { get; set; }

        [StringLength(5)]
        public string Rating { get; set; }

        [Required]     /// <--- NEW
        public string Director { get; set; }
    }

และนี่คือการย้ายข้อมูลล่าสุดของฉัน:

public partial class AddDataAnnotationsMig : DbMigration
{
    public override void Up()
    {
        AlterColumn("dbo.Movies", "Title", c => c.String(nullable: false));
        AlterColumn("dbo.Movies", "Genre", c => c.String(nullable: false));
        AlterColumn("dbo.Movies", "Rating", c => c.String(maxLength: 5));
        AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false));
    }

    public override void Down()
    {
        AlterColumn("dbo.Movies", "Director", c => c.String());
        AlterColumn("dbo.Movies", "Rating", c => c.String());
        AlterColumn("dbo.Movies", "Genre", c => c.String());
        AlterColumn("dbo.Movies", "Title", c => c.String());
    }
}

คำตอบ:


74

ถ้าฉันจำไม่ผิดสิ่งนี้ควรใช้งานได้:

AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false, defaultValueSql: "'John Doe'"));

หมายเหตุ: ค่าพารามิเตอร์ defaultValueSql ถือเป็นคำสั่ง SQL แบบคำต่อคำดังนั้นหากค่าที่ต้องการเป็นสตริงอย่างมีประสิทธิภาพเช่นเดียวกับตัวอย่างของ John Doe จำเป็นต้องมีเครื่องหมายคำพูดเดี่ยวรอบ ๆ ค่า


9
ฉันก็คิดเช่นกัน แต่ดูเหมือนจะใช้ไม่ได้กับบันทึกที่มีอยู่ ดังนั้นฉันยังคงได้รับข้อผิดพลาด
Andriy Drozdyuk

@drozzy อาจเป็นข้อผิดพลาดเช่นที่นี่: EF 4.3.1 Migration Exception - AlterColumn defaultValueSql สร้างชื่อข้อ จำกัด เริ่มต้นที่เหมือนกันสำหรับตารางที่แตกต่างกันคุณสามารถอัปเดตแถวด้วยการIS NULLตรวจสอบตามคำค้นหาของคุณ
webdeveloper

น่าสนใจ แต่ฉันไม่แน่ใจว่าฉันเข้าใจสิ่งที่พวกเขากำลังพูดถึง อย่างไรก็ตามหากนี่เป็นข้อบกพร่องใช่มันจะสมเหตุสมผล
Andriy Drozdyuk

6
ฉันคิดว่ามันควรจะเป็น: "'John Doe'"- คุณต้องใช้คำพูด SQL
ฌอน

1
@webdeveloper ฉันไม่คิดว่ามันเป็นบั๊กทำไมจะAlterColumnอัปเดตค่าปัจจุบัน เป็นคำสั่ง DDL (ไม่ใช่ DML)
Anton

110

นอกจากคำตอบจาก @webdeveloper และ @Pushpendra แล้วคุณยังต้องเพิ่มการอัปเดตการย้ายข้อมูลด้วยตนเองเพื่ออัปเดตแถวที่มีอยู่ ตัวอย่างเช่น:

public override void Up()
{
    Sql("UPDATE [dbo].[Movies] SET Title = 'No Title' WHERE Title IS NULL");
    AlterColumn("dbo.Movies", "Title", c => c.String(nullable: false,defaultValue:"MyTitle"));
}

เนื่องจากAlterColumnสร้าง DDL เพื่อตั้งค่าเริ่มต้นของคอลัมน์เป็นค่าเฉพาะบางค่าในข้อกำหนดของตาราง DDL ไม่มีผลกับแถวที่มีอยู่ในฐานข้อมูล

คุณกำลังทำการเปลี่ยนแปลงสองรายการในเวลาเดียวกัน (ตั้งค่าเริ่มต้นและทำให้คอลัมน์ไม่เป็นโมฆะ) และการเปลี่ยนแปลงแต่ละรายการสามารถใช้ได้ทีละรายการ แต่เนื่องจากคุณทำการเปลี่ยนแปลงทั้งสองในเวลาเดียวกันคุณสามารถคาดหวังว่าระบบจะ ' อย่างชาญฉลาด 'ตระหนักถึงเจตนาของคุณและตั้งค่าทั้งหมดเป็นNULLค่าเริ่มต้น แต่นี่ไม่ใช่สิ่งที่คาดหวังตลอดเวลา

สมมติว่าคุณกำหนดเฉพาะค่าเริ่มต้นสำหรับคอลัมน์และไม่ทำให้เป็นค่าว่าง เห็นได้ชัดว่าคุณไม่คาดหวังว่าระเบียน NULL ทั้งหมดจะได้รับการอัปเดตด้วยค่าเริ่มต้นที่คุณระบุ

ดังนั้นในความคิดของฉันนี่ไม่ใช่ข้อบกพร่องและฉันไม่ต้องการให้ EF อัปเดตข้อมูลของฉันด้วยวิธีที่ฉันไม่ได้บอกอย่างชัดเจนให้ทำ ผู้พัฒนามีหน้าที่สั่งระบบเกี่ยวกับสิ่งที่ต้องทำกับข้อมูล


17
สำหรับผู้ที่ค้นหาคำตอบนี้ผ่าน Google: ฉันเพิ่งลองสิ่งนี้ใน EF6 และดูเหมือนว่าคำสั่งอัปเดตจะไม่จำเป็น (อีกต่อไป) ฉันเดาว่าพวกเขาคิดว่ามันเป็นจุดบกพร่อง
EPLKleijntjens

3
ฉันสามารถรับรองได้เช่นกัน หากคุณต้องการค่าเริ่มต้นสำหรับฟิลด์ที่เป็นโมฆะเพียงแค่เปลี่ยนเป็นค่าที่ไม่เป็นโมฆะก่อนด้วยค่าเริ่มต้นจากนั้นเปลี่ยนกลับเป็นค่าว่าง มีประโยชน์มากเมื่อคุณเพิ่มฟิลด์ที่ไม่เป็นโมฆะให้กับคลาสย่อย :)
Wouter Schut

1
ระบุคำอธิบาย AlterColumn () เพียงแค่เปลี่ยนนิยามของคอลัมน์ ไม่ส่งผลกระทบต่อบันทึกที่มีอยู่ แต่อย่างใด
Korayem

10
public partial class AddDataAnnotationsMig : DbMigration
{
    public override void Up()
    {
        AlterColumn("dbo.Movies", "Title", c => c.String(nullable: false,defaultValue:"MyTitle"));
        AlterColumn("dbo.Movies", "Genre", c => c.String(nullable: false,defaultValue:"Genre"));
        AlterColumn("dbo.Movies", "Rating", c => c.String(maxLength: 5));
        AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false,defaultValue:"Director"));

    }

    public override void Down()
    {       
        AlterColumn("dbo.Movies", "Director", c => c.String());
        AlterColumn("dbo.Movies", "Rating", c => c.String());
        AlterColumn("dbo.Movies", "Genre", c => c.String());
        AlterColumn("dbo.Movies", "Title", c => c.String());       
    }
}

2
อืม ... ขอบคุณครับ แต่คำตอบของ @ webdeveloper แตกต่างกันอย่างไร
Andriy Drozdyuk

1
มันไม่ได้บอกคุณว่าคุณต้องเพิ่มพารามิเตอร์ค่าเริ่มต้น
ที่ไหน

1
@Pushpendra เป็นเรื่องตลกที่นักพัฒนามักจะลืมว่ากาลครั้งหนึ่งพวกเขาไม่รู้อะไรมากนัก ฉันชอบคำตอบโดยละเอียดที่ตอบสนองทุกระดับ เก่งมาก!
ประโยชน์

5

ไม่แน่ใจว่าตัวเลือกนี้มีอยู่ตลอดเวลาหรือไม่ แต่เพิ่งพบปัญหาที่คล้ายกันพบว่าฉันสามารถตั้งค่าเริ่มต้นได้โดยไม่ต้องเรียกใช้การอัปเดตด้วยตนเองโดยใช้สิ่งต่อไปนี้

defaultValueSql: "'NY'"

ฉันได้รับข้อผิดพลาดเมื่อค่าที่ระบุคือ"NY"จากนั้นฉันก็รู้ว่าพวกเขาคาดหวังค่า SQL เช่น"GETDATE()"นั้นฉันจึงลอง"'NY'"และนั่นก็เป็นเคล็ดลับ

ทั้งบรรทัดจะเป็นแบบนี้

AddColumn("TABLE_NAME", "State", c => c.String(maxLength: 2, nullable: false, defaultValueSql: "'NY'"));

ขอบคุณคำตอบนี้ทำให้ฉันมาถูกทาง


2

ฉันพบว่าการใช้ Auto-Property Initializer กับคุณสมบัติของเอนทิตีก็เพียงพอแล้วที่จะทำให้งานสำเร็จ

ตัวอย่างเช่น:

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

2
เป็นคำตอบที่ดี (ช่วยฉันด้วย) แต่นี่ไม่ได้เพิ่มค่าเริ่มต้นในฐานข้อมูลมันตั้งค่าเป็นรหัส
chris31389

ใช่มันไม่ได้เพิ่มค่าเริ่มต้นในฐานข้อมูลหลังจาก chnages การโยกย้าย
Chetan Chaudhari

2

คำตอบอื่น ๆ จำนวนมากมุ่งเน้นไปที่วิธีการแทรกแซงด้วยตนเองเมื่อเกิดปัญหาเหล่านี้

หลังจากสร้างการย้ายข้อมูลแล้วให้ดำเนินการเปลี่ยนแปลงอย่างใดอย่างหนึ่งต่อไปนี้กับการโอนย้าย:

  1. ปรับเปลี่ยนนิยามคอลัมน์เพื่อรวมคำสั่ง defaultValue หรือ defaultSql:
    AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false, default: ""));

  2. ใส่คำสั่ง SQL เพื่อเติมคอลัมน์ที่มีอยู่ก่อน AlterColumn:
    Sql("UPDATE dbo.Movies SET Director = '' WHERE Director IS NULL");

โปรดทราบว่าการเปลี่ยนแปลงด้วยตนเองที่ใช้กับสคริปต์การย้ายข้อมูลจะถูกเขียนทับหากคุณปรับเปลี่ยนการย้ายข้อมูลอีกครั้ง สำหรับวิธีแก้ปัญหาแรกการขยาย EF เพื่อกำหนดค่าเริ่มต้นบนฟิลด์โดยอัตโนมัติเป็นส่วนหนึ่งของการสร้างการย้ายข้อมูลนั้นค่อนข้างง่าย

หมายเหตุ: EF ไม่ทำสิ่งนี้ให้คุณโดยอัตโนมัติเนื่องจากการใช้ค่าเริ่มต้นจะแตกต่างกันไปสำหรับผู้ให้บริการ RDBMS แต่ละราย แต่เนื่องจากค่าเริ่มต้นมีความหมายน้อยกว่าในรันไทม์ EF ที่บริสุทธิ์เนื่องจากการแทรกแต่ละแถวจะให้ค่าปัจจุบันสำหรับคุณสมบัติ แม้ว่าจะเป็นโมฆะดังนั้นข้อ จำกัด ของค่าเริ่มต้นจะไม่ถูกประเมิน
คำสั่ง AlterColumn นี้เป็นครั้งเดียวที่ข้อ จำกัด เริ่มต้นเข้ามามีบทบาทฉันเดาว่านี่เป็นลำดับความสำคัญที่ต่ำกว่าสำหรับทีมที่ออกแบบการใช้งานการโยกย้ายเซิร์ฟเวอร์ SQL

โซลูชันต่อไปนี้รวมสัญกรณ์แอตทริบิวต์รูปแบบการกำหนดค่าแบบจำลองและคำอธิบายประกอบคอลัมน์เพื่อส่งผ่านข้อมูลเมตาไปยังตัวสร้างรหัสการย้ายข้อมูล ขั้นตอนที่ 1 และ 2 สามารถแทนที่ด้วยสัญกรณ์แบบคล่องแคล่วสำหรับแต่ละฟิลด์ที่ได้รับผลกระทบหากคุณไม่ได้ใช้สัญกรณ์แอตทริบิวต์
มีเทคนิคมากมายในการเล่นที่นี่อย่าลังเลที่จะใช้บางส่วนหรือทั้งหมดฉันหวังว่าจะมีค่าสำหรับทุกคนที่นี่


  1. ประกาศค่าเริ่มต้น
    สร้างหรือกำหนดค่าแอตทริบิวต์ที่มีอยู่ใหม่เพื่อกำหนดค่าเริ่มต้นที่จะใช้สำหรับตัวอย่างนี้เราจะสร้างแอตทริบิวต์ใหม่ที่เรียกว่า DefaultValue ที่สืบทอดมาจาก ComponentModel.DefaultValueAttribute เนื่องจากการใช้งานนั้นง่ายและมีโอกาสที่จะมีอยู่ ฐานรหัสใช้แอตทริบิวต์นี้แล้ว ด้วยการใช้งานนี้คุณจะต้องใช้แอตทริบิวต์เฉพาะนี้เพื่อเข้าถึง DefaultValueSql ซึ่งมีประโยชน์สำหรับวันที่และสถานการณ์ที่กำหนดเองอื่น ๆ

    การนำไปใช้

    [DefaultValue("Insert DefaultValue Here")]
    [Required]     /// <--- NEW
    public string Director { get; set; }
    
    // Example of default value sql
    [DefaultValue(DefaultValueSql: "GetDate()")]
    [Required]
    public string LastModified { get; set; }
    

    นิยามแอตทริบิวต์

    namespace EFExtensions
    {
        /// <summary>
        /// Specifies the default value for a property but allows a custom SQL statement to be provided as well. <see cref="MiniTuber.Database.Conventions.DefaultValueConvention"/>
        /// </summary>
        public class DefaultValueAttribute : System.ComponentModel.DefaultValueAttribute
        {
            /// <summary>
            /// Specifies the default value for a property but allows a custom SQL statement to be provided as well. <see cref="MiniTuber.Database.Conventions.DefaultValueConvention"/>
            /// </summary>
            public DefaultValueAttribute() : base("")
            {
            }
    
            /// <i
            /// <summary>
            /// Optional SQL to use to specify the default value.
            /// </summary>
            public string DefaultSql { get; set; }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class using a Unicode character.
            /// </summary>
            /// <param name="value">
            /// A Unicode character that is the default value.
            /// </param>
            public DefaultValueAttribute(char value) : base(value) { }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class using an 8-bit unsigned integer.
            /// </summary>
            /// <param name="value">
            /// An 8-bit unsigned integer that is the default value.
            /// </param>
            public DefaultValueAttribute(byte value) : base(value) { }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class using a 16-bit signed integer.
            /// </summary>
            /// <param name="value">
            /// A 16-bit signed integer that is the default value.
            /// </param>
            public DefaultValueAttribute(short value) : base(value) { }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class using a 32-bit signed integer.
            /// </summary>
            /// <param name="value">
            /// A 32-bit signed integer that is the default value.
            /// </param>
            public DefaultValueAttribute(int value) : base(value) { }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class using a 64-bit signed integer.
            /// </summary>
            /// <param name="value">
            /// A 64-bit signed integer that is the default value.
            /// </param>
            public DefaultValueAttribute(long value) : base(value) { }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class using a single-precision floating point number.
            /// </summary>
            /// <param name="value">
            /// A single-precision floating point number that is the default value.
            /// </param>
            public DefaultValueAttribute(float value) : base(value) { }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class using a double-precision floating point number.
            /// </summary>
            /// <param name="value">
            /// A double-precision floating point number that is the default value.
            /// </param>
            public DefaultValueAttribute(double value) : base(value) { }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class using a System.Boolean value.
            /// </summary>
            /// <param name="value">
            /// A System.Boolean that is the default value.
            /// </param>
            public DefaultValueAttribute(bool value) : base(value) { }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class using a System.String.
            /// </summary>
            /// <param name="value">
            /// A System.String that is the default value.
            /// </param>
            public DefaultValueAttribute(string value) : base(value) { }
    
            /// <summary>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class.
            /// </summary>
            /// <param name="value">
            /// An System.Object that represents the default value.
            /// </param>
            public DefaultValueAttribute(object value) : base(value) { }
    
            /// /// <inheritdoc/>
            /// Initializes a new instance of the System.ComponentModel.DefaultValueAttribute
            /// class, converting the specified value to the specified type, and using an invariant
            /// culture as the translation context.
            /// </summary>
            /// <param name="type">
            /// A System.Type that represents the type to convert the value to.
            /// </param>
            /// <param name="value">
            /// A System.String that can be converted to the type using the System.ComponentModel.TypeConverter
            /// for the type and the U.S. English culture.
            /// </param>
            public DefaultValueAttribute(Type type, string value) : base(value) { }
        }
    }
    
  2. สร้างแบบแผนเพื่อแทรกค่าเริ่มต้นลงในคำอธิบายประกอบคอลัมน์คำอธิบายประกอบ
    คอลัมน์ใช้เพื่อส่งผ่านข้อมูลเมตาที่กำหนดเองเกี่ยวกับคอลัมน์ไปยังตัวสร้างสคริปต์การย้ายข้อมูล
    การใช้รูปแบบในการดำเนินการนี้แสดงให้เห็นถึงพลังที่อยู่เบื้องหลังสัญกรณ์แอตทริบิวต์เพื่อลดความซับซ้อนของวิธีการกำหนดและจัดการข้อมูลเมตาที่คล่องแคล่วสำหรับคุณสมบัติจำนวนมากแทนที่จะระบุทีละฟิลด์สำหรับแต่ละฟิลด์

    namespace EFExtensions
    {
    
        /// <summary>
        /// Implement SQL Default Values from System.ComponentModel.DefaultValueAttribute
        /// </summary>
        public class DefaultValueConvention : Convention
        {
            /// <summary>
            /// Annotation Key to use for Default Values specified directly as an object
            /// </summary>
            public const string DirectValueAnnotationKey = "DefaultValue";
            /// <summary>
            /// Annotation Key to use for Default Values specified as SQL Strings
            /// </summary>
            public const string SqlValueAnnotationKey = "DefaultSql";
    
            /// <summary>
            /// Implement SQL Default Values from System.ComponentModel.DefaultValueAttribute
            /// </summary>
            public DefaultValueConvention()
            {
                // Implement SO Default Value Attributes first
                this.Properties()
                        .Where(x => x.HasAttribute<EFExtensions.DefaultValueAttribute>())
                        .Configure(c => c.HasColumnAnnotation(
                            c.GetAttribute<EFExtensions.DefaultValueAttribute>().GetDefaultValueAttributeKey(),
                            c.GetAttribute<EFExtensions.DefaultValueAttribute>().GetDefaultValueAttributeValue()
                            ));
    
                // Implement Component Model Default Value Attributes, but only if it is not the SO implementation
                this.Properties()
                        .Where(x => x.HasAttribute<System.ComponentModel.DefaultValueAttribute>())
                        .Where(x => !x.HasAttribute<MiniTuber.DataAnnotations.DefaultValueAttribute>())
                        .Configure(c => c.HasColumnAnnotation(
                            DefaultValueConvention.DirectValueAnnotationKey, 
                            c.GetAttribute<System.ComponentModel.DefaultValueAttribute>().Value
                            ));
            }
        }
    
        /// <summary>
        /// Extension Methods to simplify the logic for building column annotations for Default Value processing
        /// </summary>
        public static partial class PropertyInfoAttributeExtensions
        {
            /// <summary>
            /// Wrapper to simplify the lookup for a specific attribute on a property info.
            /// </summary>
            /// <typeparam name="T">Type of attribute to lookup</typeparam>
            /// <param name="self">PropertyInfo to inspect</param>
            /// <returns>True if an attribute of the requested type exists</returns>
            public static bool HasAttribute<T>(this PropertyInfo self) where T : Attribute
            {
                return self.GetCustomAttributes(false).OfType<T>().Any();
            }
    
            /// <summary>
            /// Wrapper to return the first attribute of the specified type
            /// </summary>
            /// <typeparam name="T">Type of attribute to return</typeparam>
            /// <param name="self">PropertyInfo to inspect</param>
            /// <returns>First attribuite that matches the requested type</returns>
            public static T GetAttribute<T>(this System.Data.Entity.ModelConfiguration.Configuration.ConventionPrimitivePropertyConfiguration self) where T : Attribute
            {
                return self.ClrPropertyInfo.GetCustomAttributes(false).OfType<T>().First();
            }
    
            /// <summary>
            /// Helper to select the correct DefaultValue annotation key based on the attribute values
            /// </summary>
            /// <param name="self"></param>
            /// <returns></returns>
            public static string GetDefaultValueAttributeKey(this EFExtensions.DefaultValueAttribute self)
            {
                return String.IsNullOrWhiteSpace(self.DefaultSql) ? DefaultValueConvention.DirectValueAnnotationKey : DefaultValueConvention.SqlValueAnnotationKey;
            }
    
            /// <summary>
            /// Helper to select the correct attribute property to send as a DefaultValue annotation value
            /// </summary>
            /// <param name="self"></param>
            /// <returns></returns>
            public static object GetDefaultValueAttributeValue(this EFExtensions.DefaultValueAttribute self)
            {
                return String.IsNullOrWhiteSpace(self.DefaultSql) ? self.Value : self.DefaultSql;
            }
        }
    
    }
    
  3. เพิ่มอนุสัญญาไปยัง DbContext
    มีหลายวิธีในการบรรลุสิ่งนี้ฉันต้องการประกาศอนุสัญญาเป็นขั้นตอนแรกที่กำหนดเองในตรรกะ ModelCreation ของฉันซึ่งจะอยู่ในคลาส DbContext ของคุณ

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        // Use our new DefaultValueConvention
        modelBuilder.Conventions.Add<EFExtensions.DefaultValueConvention>();
    
        // My personal favourites ;)
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
    
    }
    
  4. แทนที่ MigrationCodeGenerator
    เมื่อคำอธิบายประกอบเหล่านั้นถูกนำไปใช้กับคำจำกัดความของคอลัมน์ภายในโมเดลแล้วเราจำเป็นต้องแก้ไขตัวสร้างสคริปต์การย้ายข้อมูลเพื่อใช้คำอธิบายประกอบเหล่านั้น สำหรับสิ่งนี้เราจะสืบทอดจากสิ่งSystem.Data.Entity.Migrations.Design.CSharpMigrationCodeGeneratorที่เราต้องฉีดเพียงเล็กน้อยของการเปลี่ยนแปลง
    เมื่อเราประมวลผลคำอธิบายประกอบที่กำหนดเองของเราแล้วเราจำเป็นต้องลบออกจากคำจำกัดความของคอลัมน์เพื่อป้องกันไม่ให้ถูกต่อเนื่องกับผลลัพธ์สุดท้าย

    ดูรหัสคลาสพื้นฐานเพื่อสำรวจการใช้งานอื่น ๆ : http://entityframework.codeplex.com/sourcecontrol/latest#src/EntityFramework/Migrations/Design/CSharpMigrationCodeGenerator.cs

    namespace EFExtensions
    {
        /// <summary>
        /// Implement DefaultValue constraint definition in Migration Scripts.
        /// </summary>
        /// <remarks>
        /// Original guide that provided inspiration for this https://romiller.com/2012/11/30/code-first-migrations-customizing-scaffolded-code/
        /// </remarks>
        public class CustomCodeGenerator : System.Data.Entity.Migrations.Design.CSharpMigrationCodeGenerator
        {
            /// <summary>
            /// Inject Default values from the DefaultValue attribute, if the DefaultValueConvention has been enabled.
            /// </summary>
            /// <seealso cref="DefaultValueConvention"/>
            /// <param name="column"></param>
            /// <param name="writer"></param>
            /// <param name="emitName"></param>
            protected override void Generate(ColumnModel column, IndentedTextWriter writer, bool emitName = false)
            {
                var annotations = column.Annotations?.ToList();
                if (annotations != null && annotations.Any())
                {
                    for (int index = 0; index < annotations.Count; index ++)
                    {
                        var annotation = annotations[index];
                        bool handled = true;
    
                        try
                        {
                            switch (annotation.Key)
                            {
                                case DefaultValueConvention.SqlValueAnnotationKey:
                                    if (annotation.Value?.NewValue != null)
                                    {
                                        column.DefaultValueSql = $"{annotation.Value.NewValue}";
                                    }
                                    break;
                                case DefaultValueConvention.DirectValueAnnotationKey:
                                    if (annotation.Value?.NewValue != null)
                                    {
                                        column.DefaultValue = Convert.ChangeType(annotation.Value.NewValue, column.ClrType);
                                    }
                                    break;
                                default:
                                    handled = false;
                                    break;
                            }
                        }
                        catch(Exception ex)
                        {
                            // re-throw with specific debug information
                            throw new ApplicationException($"Failed to Implement Column Annotation for column: {column.Name} with key: {annotation.Key} and new value: {annotation.Value.NewValue}", ex);
                        }
    
                        if(handled)
                        {
                            // remove the annotation, it has been applied
                            column.Annotations.Remove(annotation.Key);
                        }
                    }
                }
                base.Generate(column, writer, emitName);
            }
    
            /// <summary>
            /// Generates class summary comments and default attributes
            /// </summary>
            /// <param name="writer"> Text writer to add the generated code to. </param>
            /// <param name="designer"> A value indicating if this class is being generated for a code-behind file. </param>
            protected override void WriteClassAttributes(IndentedTextWriter writer, bool designer)
            {
                writer.WriteLine("/// <summary>");
                writer.WriteLine("/// Definition of the Migration: {0}", this.ClassName);
                writer.WriteLine("/// </summary>");
                writer.WriteLine("/// <remarks>");
                writer.WriteLine("/// Generated Time: {0}", DateTime.Now);
                writer.WriteLine("/// Generated By: {0}", Environment.UserName);
                writer.WriteLine("/// </remarks>");
                base.WriteClassAttributes(writer, designer);
            }
    
    
        }
    }
    
  5. ลงทะเบียน CustomCodeGenerator
    ขั้นตอนสุดท้ายในไฟล์ DbMigration Configuration เราต้องระบุ Code Generator ที่จะใช้ค้นหา Configuration.cs ในโฟลเดอร์ Migration ตามค่าเริ่มต้น ...

    internal sealed class Configuration : DbMigrationsConfiguration<YourApplication.Database.Context>
    {
        public Configuration()
        {
            // I recommend that auto-migrations be disabled so that we control
            // the migrations explicitly 
            AutomaticMigrationsEnabled = false;
            CodeGenerator = new EFExtensions.CustomCodeGenerator();
        }
    
        protected override void Seed(YourApplication.Database.Context context)
        {
            //   Your custom seed logic here
        }
    }
    

2

ตั้งแต่ EF Core 2.1 คุณสามารถใช้MigrationBuilder.UpdateDataเพื่อเปลี่ยนค่าก่อนที่จะแก้ไขคอลัมน์ (สะอาดกว่าการใช้ SQL ดิบ):

protected override void Up(MigrationBuilder migrationBuilder)
{
    // Change existing NULL values to NOT NULL values
    migrationBuilder.UpdateData(
        table: tableName,
        column: columnName,
        value: valueInsteadOfNull,
        keyColumn: columnName,
        keyValue: null);

    // Change column type to NOT NULL
    migrationBuilder.AlterColumn<ColumnType>(
        table: tableName,
        name: columnName,
        nullable: false,
        oldClrType: typeof(ColumnType),
        oldNullable: true);
}

0

ด้วยเหตุผลบางประการฉันไม่สามารถอธิบายตัวเองได้ว่าคำตอบที่ได้รับอนุมัติใช้ไม่ได้กับฉันอีกต่อไป

มันใช้งานได้กับแอพอื่นในแอพที่ฉันใช้งานไม่ได้

ดังนั้นทางเลือกอื่น แต่ค่อนข้างไม่มีประสิทธิภาพวิธีแก้ปัญหาคือการแทนที่ SaveChanges () วิธีการดังที่แสดงไว้ด้านล่าง วิธีนี้ควรอยู่ในคลาสบริบท

    public override int SaveChanges()
    {
        foreach (var entry in ChangeTracker.Entries().Where(entry => entry.Entity.GetType().GetProperty("ColumnName") != null))
        {
            if (entry.State == EntityState.Added)
            {
                entry.Property("ColumnName").CurrentValue = "DefaultValue";
            }
        }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.