J'ai ajouté l'annotation de données [Required]
à l'un de mes modèles dans une application ASP.NET MVC . Après avoir créé une migration, l'exécution de la commande Update-Database
entraîne l'erreur suivante:
Impossible d'insérer la valeur NULL dans la colonne 'Director', table 'MOVIES_cf7bad808fa94f89afa2e5dae1161e78.dbo.Movies'; la colonne n'en a pas autoriser les valeurs nulles. UPDATE échoue. La déclaration a été terminée.
Cela est dû au fait que certains enregistrements ont NULL dans leurs colonnes Director
. Comment puis-je changer automatiquement ces valeurs en un directeur par défaut (dites "John Doe")?
Voici mon modèle:
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; }
}
et voici ma dernière migration:
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());
}
}
Si je me souviens bien, quelque chose comme ceci devrait marcher:
AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false, defaultValueSql: "John Doe"));
Outre les réponses de @webdeveloper et @Pushpendra, vous devez ajouter manuellement les mises à jour de votre migration pour mettre à jour les lignes existantes. Par exemple:
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"));
}
En effet, AlterColumn
génère DDL pour définir la valeur par défaut de la colonne sur une valeur spécifique dans la spécification de la table. Le DDL n'affecte pas les lignes existantes de la base de données.
En fait, vous apportez deux modifications en même temps (définir la valeur par défaut et rendre la colonne NOT NULL) et chacune d’elles est valide individuellement, mais puisque vous les effectuez en même temps, vous pouvez vous attendre à ce que le système " "intelligemment" réaliser votre intention et définir toutes les valeurs NULL
à la valeur par défaut, mais ce n'est pas ce qui est attendu tout le temps.
Supposons que vous définissiez uniquement la valeur par défaut pour la colonne et que vous ne la rendiez pas NOT NULL. Évidemment, vous ne vous attendez pas à ce que tous les enregistrements NULL soient mis à jour avec les valeurs par défaut que vous fournissez.
Donc, à mon avis, il ne s'agit pas d'un bug et je ne veux pas que EF mette à jour mes données de la manière que je ne lui dis pas explicitement. Le développeur est responsable d'informer le système de l'utilisation des données.
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());
}
}
J'ai constaté que le simple fait d'utiliser Auto-Property Initializer sur une propriété d'entité est suffisant pour faire le travail.
Par exemple:
public class Thing {
public bool IsBigThing { get; set; } = false;
}
Depuis EF Core 2.1, vous pouvez utiliser MigrationBuilder.UpdateData
pour modifier les valeurs avant de modifier la colonne (plus propre que d'utiliser du SQL brut):
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);
}
Pour une raison quelconque, que je n'ai pas pu m'expliquer, la réponse approuvée ne fonctionne plus pour moi.
Cela a fonctionné sur une autre application, celle que je travaille ne fonctionne pas.
Ainsi, une solution alternative, mais assez inefficace, consisterait à remplacer la méthode SaveChanges () comme indiqué ci-dessous. Cette méthode doit être sur la classe de contexte.
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";
}
}