Nilai default untuk bidang Wajib di migrasi Kerangka Kerja Entitas?

92

Saya telah menambahkan [Required]anotasi data ke salah satu model saya dalam aplikasi ASP.NET MVC . Setelah membuat migrasi, menjalankan Update-Databaseperintah menghasilkan kesalahan berikut:

Tidak dapat memasukkan nilai NULL ke dalam kolom 'Director', table 'MOVIES_cf7bad808fa94f89afa2e5dae1161e78.dbo.Movies'; kolom tidak mengizinkan null. UPDATE gagal. Pernyataan tersebut telah dihentikan.

Ini karena beberapa record memiliki NULL di Directorkolomnya. Bagaimana saya bisa secara otomatis mengubah nilai-nilai itu ke beberapa default (katakanlah "John Doe") direktur?

Ini model saya:

  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; }
    }

dan ini migrasi terakhir saya:

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());
    }
}
Andriy Drozdyuk
sumber

Jawaban:

74

Jika saya ingat dengan benar, sesuatu seperti ini seharusnya berfungsi:

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

Catatan: Nilai parameter defaultValueSql diperlakukan sebagai pernyataan SQL verbatim, jadi jika nilai yang diperlukan secara efektif adalah string, seperti contoh John Doe, maka tanda kutip tunggal diperlukan di sekitar nilai.

pengembang web
sumber
9
Saya juga berpikir begitu, tapi sepertinya itu tidak berhasil untuk rekaman yang sudah ada. Jadi saya masih mendapatkan kesalahan.
Andriy Drozdyuk
@drozzy Mungkin ini bug, seperti di sini: EF 4.3.1 Migration Exception - AlterColumn defaultValueSql membuat nama batasan default yang sama untuk tabel berbeda Anda dapat memperbarui baris dengan IS NULLcheck by query Anda.
pengembang web
Menarik, tetapi saya tidak yakin saya mengerti apa yang mereka bicarakan. Namun, jika ini adalah bug, maka ya, itu masuk akal.
Andriy Drozdyuk
6
Saya pikir itu seharusnya: "'John Doe'"- Anda perlu menggunakan kutipan SQL.
Sean
1
@webdeveloper, menurut saya ini bukan bug, mengapa AlterColumnmemperbarui nilai saat ini? Ini adalah perintah DDL (bukan DML).
Anton
110

Selain jawaban dari @webdeveloper dan @Pushpendra, Anda perlu menambahkan pembaruan ke migrasi secara manual untuk memperbarui baris yang ada. Sebagai contoh:

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"));
}

Ini karena AlterColumnmenghasilkan DDL untuk mengatur default kolom ke beberapa nilai tertentu dalam spesifikasi tabel. DDL tidak memengaruhi baris yang ada dalam database.

Anda sebenarnya membuat dua perubahan pada saat yang sama (menyetel default dan menjadikan kolom NOT NULL) dan masing-masing valid secara individual, tetapi karena Anda membuat keduanya pada saat yang sama, Anda dapat mengharapkan sistem untuk ' dengan cerdas 'menyadari maksud Anda dan menyetel semua NULLnilai ke nilai default, tetapi ini bukanlah yang diharapkan sepanjang waktu.

Misalkan Anda hanya menyetel nilai default untuk kolom, dan tidak menjadikannya NOT NULL. Anda jelas tidak mengharapkan semua catatan NULL diperbarui dengan default yang Anda berikan.

Jadi, menurut saya, ini bukan bug, dan saya tidak ingin EF memperbarui data saya dengan cara yang tidak saya perintahkan secara eksplisit. Pengembang bertanggung jawab untuk menginstruksikan sistem tentang apa yang harus dilakukan dengan data.

Iravanchi
sumber
17
Untuk orang-orang yang menemukan jawaban ini melalui google: Saya baru saja mencoba ini di EF6 dan pernyataan pembaruan sepertinya tidak diperlukan (lagi). Saya kira mereka menganggapnya bug.
EPLKleijntjens
3
Saya juga bisa menjamin itu. Jika Anda memerlukan nilai default bahkan untuk bidang nullable, ubah saja menjadi tidak-nullable terlebih dahulu dengan nilai default, lalu ubah kembali ke nullable. Sangat berguna ketika Anda menambahkan bidang non-nullable ke kelas anak :)
Wouter Schut
1
Tepat penjelasan. AlterColumn () hanya mengubah definisi kolom. Itu tidak mempengaruhi rekaman yang ada sama sekali
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());       
    }
}
Pushpendra
sumber
2
Um ... terima kasih, tapi apa bedanya dengan jawaban @ webdeveloper?
Andriy Drozdyuk
1
itu tidak memberi tahu Anda di mana Anda harus menambahkan parameter nilai default
Pushpendra
1
@Pushpendra, lucu sekali bagaimana developer cenderung lupa bahwa pada suatu waktu, mereka tidak tahu banyak. Saya suka jawaban rinci yang memuaskan semua tingkatan. Kerja bagus!
BergunaBee
5

tidak yakin apakah opsi ini selalu ada tetapi baru saja mengalami masalah serupa, menemukan bahwa saya dapat mengatur nilai default tanpa menjalankan pembaruan manual menggunakan yang berikut ini

defaultValueSql: "'NY'"

Saya mendapat kesalahan ketika nilai yang diberikan "NY"kemudian saya menyadari bahwa mereka mengharapkan nilai SQL seperti "GETDATE()"jadi saya mencoba "'NY'"dan itu berhasil

seluruh baris terlihat seperti ini

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

Berkat jawaban ini , membuat saya di jalur yang benar

workabyte
sumber
2

Saya menemukan bahwa hanya menggunakan Penginisialisasi Properti Otomatis pada properti entitas sudah cukup untuk menyelesaikan pekerjaan.

Sebagai contoh:

public class Thing {
    public bool IsBigThing { get; set; } = false;
}
Velyo
sumber
2
Ini adalah jawaban yang bagus (membantu saya), tetapi ini tidak menambahkan nilai default dalam database, ini menetapkan nilai dalam kode.
chris31389
benar itu tidak menambahkan nilai default dalam database setelah chnage migrasi
Chetan Chaudhari
2

Banyak dari tanggapan lain yang berfokus pada cara melakukan intervensi secara manual saat masalah ini terjadi.

Setelah membuat Migrasi, lakukan salah satu perubahan berikut pada migrasi:

  1. Ubah definisi Kolom untuk menyertakan pernyataan defaultValue atau defaultSql:
    AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false, default: ""));

  2. Masukkan pernyataan SQL untuk mengisi kolom yang ada, sebelum AlterColumn:
    Sql("UPDATE dbo.Movies SET Director = '' WHERE Director IS NULL");

Perlu diingat bahwa perubahan manual yang diterapkan ke skrip migrasi akan ditimpa jika Anda menata ulang migrasi. Untuk solusi pertama, cukup mudah untuk memperluas EF untuk menentukan nilai default pada bidang secara otomatis sebagai bagian dari generasi migrasi.

CATATAN: EF tidak secara otomatis melakukan ini untuk Anda karena implementasi nilai default akan berbeda untuk setiap penyedia RDBMS, tetapi juga karena nilai default memiliki arti yang lebih sedikit dalam runtime EF murni karena setiap penyisipan baris akan memberikan nilai saat ini untuk setiap properti, meskipun nilainya nol, sehingga batasan nilai default tidak pernah dievaluasi.
Pernyataan AlterColumn ini adalah satu-satunya saat kendala default mulai berlaku, saya rasa ini menjadi prioritas yang lebih rendah untuk tim yang merancang Implementasi Migrasi SQL Server.

Solusi berikut menggabungkan notasi atribut, konvensi konfigurasi model, dan anotasi kolom untuk meneruskan metadata ke generator kode Migrasi kustom. Langkah 1 dan 2 dapat diganti dengan notasi fasih untuk setiap bidang yang terpengaruh jika Anda tidak menggunakan notasi atribut.
Ada banyak teknik dalam bermain di sini, silakan gunakan beberapa atau semua, saya harap ada nilai untuk semua orang di sini


  1. Deklarasikan Nilai Default
    Buat atau gunakan kembali atribut yang ada untuk menentukan nilai default yang akan digunakan, untuk contoh ini kita akan membuat atribut baru bernama DefaultValue yang mewarisi dari ComponentModel.DefaultValueAttribute, karena penggunaannya intuitif dan ada kemungkinan basis kode sudah menerapkan atribut ini. Dengan implementasi ini, Anda hanya perlu menggunakan atribut khusus ini untuk mengakses DefaultValueSql yang berguna untuk tanggal dan skenario khusus lainnya.

    Penerapan

    [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; }
    

    Definisi Atribut

    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. Buat konvensi untuk memasukkan nilai default ke dalam anotasi kolom Anotasi
    kolom digunakan untuk meneruskan metadata khusus tentang kolom ke generator skrip migrasi.
    Menggunakan konvensi untuk melakukan ini mendemonstrasikan kekuatan di balik notasi atribut untuk menyederhanakan bagaimana metadata yang lancar dapat didefinisikan dan dimanipulasi untuk banyak properti daripada menentukannya secara individual untuk setiap bidang.

    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. Tambahkan Konvensi ke DbContext
    Ada banyak cara untuk mencapai ini, saya ingin mendeklarasikan konvensi sebagai langkah kustom pertama dalam logika ModelCreation saya, ini akan berada di kelas DbContext Anda.

    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. Mengganti MigrationCodeGenerator
    Setelah anotasi tersebut telah diterapkan ke definisi kolom dalam model, kita perlu memodifikasi generator skrip migrasi untuk menggunakan anotasi tersebut. Untuk ini kita akan mewarisi dari System.Data.Entity.Migrations.Design.CSharpMigrationCodeGeneratorkarena kita hanya perlu menyuntikkan sedikit perubahan.
    Setelah kami memproses anotasi khusus kami, kami perlu menghapusnya dari definisi kolom untuk mencegahnya diserialkan ke hasil akhir.

    Lihat kode kelas dasar untuk menjelajahi penggunaan lain: 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. Daftarkan CustomCodeGenerator
    Langkah terakhir, di file Konfigurasi DbMigration kita perlu menentukan Penghasil Kode yang akan digunakan, cari Configuration.cs di folder Migrasi Anda secara default ...

    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
        }
    }
    
Chris Schaller
sumber
2

Sejak EF Core 2.1, Anda dapat menggunakan MigrationBuilder.UpdateDatauntuk mengubah nilai sebelum mengubah kolom (lebih bersih daripada menggunakan SQL mentah):

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);
}
Antoine Robin
sumber
0

Untuk beberapa alasan, saya tidak dapat menjelaskan diri saya sendiri, jawaban yang disetujui tidak lagi berfungsi untuk saya.

Ini berfungsi di aplikasi lain, di aplikasi yang saya kerjakan itu tidak.

Jadi, solusi alternatif, tetapi cukup tidak efisien , akan menimpa Metode SaveChanges () seperti yang ditunjukkan di bawah ini. Metode ini harus berada di kelas Konteks.

    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";
            }
        }
Liviu Sosu
sumber