271 votes

Entity Framework 6 Code first Valeur par défaut

Y a-t-il un moyen "élégant" de donner une valeur par défaut à une propriété spécifique ?

Peut-être par DataAnnotations, quelque chose comme :

[DefaultValue("true")]
public bool Active { get; set; }

Merci.

197voto

devi Points 1008

Vous pouvez le faire en modifiant manuellement le code de la première migration :

public override void Up()
{    
   AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}

102voto

ravinsp Points 1456

Cela fait un moment, mais je laisse une note pour les autres. J'ai accompli ce qui est nécessaire avec un attribut et j'ai décoré les champs de ma classe de modèle avec cet attribut comme je le souhaite.

[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }

J'ai eu l'aide de ces 2 articles :

Ce que j'ai fait :

Définir l'attribut

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
    public string DefaultValue { get; set; }
}

Dans le "OnModelCreating" du contexte

modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));

Dans le générateur SQL personnalisé

private void SetAnnotatedColumn(ColumnModel col)
{
    AnnotationValues values;
    if (col.Annotations.TryGetValue("SqlDefaultValue", out values))
    {
         col.DefaultValueSql = (string)values.NewValue;
    }
}

Ensuite, dans le constructeur de la configuration des migrations, enregistrer le générateur SQL personnalisé.

SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());

96voto

Les réponses ci-dessus ont vraiment aidé, mais n'ont fourni qu'une partie de la solution. Le problème majeur est que dès que vous supprimez l'attribut de valeur par défaut, la contrainte sur la colonne dans la base de données ne sera pas supprimée. Ainsi, la valeur par défaut précédente restera dans la base de données.

Voici une solution complète au problème, comprenant la suppression des contraintes SQL lors de la suppression de l'attribut. J'utilise également l'attribut DefaultValue natif du framework .NET.

Utilisation

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }

Pour que cela fonctionne, vous devez mettre à jour les fichiers IdentityModels.cs et Configuration.cs

Fichier IdentityModels.cs

Ajoutez/mettez à jour cette méthode dans votre classe ApplicationDbContext

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
            base.OnModelCreating(modelBuilder);
            var convention = new AttributeToColumnAnnotationConvention("SqlDefaultValue", (p, attributes) => attributes.SingleOrDefault().Value.ToString());
            modelBuilder.Conventions.Add(convention);
}

Fichier Configuration.cs

Mettez à jour le constructeur de votre classe Configuration en enregistrant un générateur SQL personnalisé, comme ceci:

internal sealed class Configuration : DbMigrationsConfiguration
{
    public Configuration()
    {
        // Générateur SQL de valeur par défaut
        SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
    }
}

Ensuite, ajoutez une classe de générateur SQL personnalisé (vous pouvez l'ajouter au fichier Configuration.cs ou à un fichier séparé)

internal class DefaultValueSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    private int dropConstraintCount;

    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)
    {
        if (column.Annotations.TryGetValue("SqlDefaultValue", out var values))
        {
            if (values.NewValue == null)
            {
                column.DefaultValueSql = null;
                using var writer = Writer();

                // Suppression de la contrainte
                writer.WriteLine(GetSqlDropConstraintQuery(tableName, column.Name));
                Statement(writer);
            }
            else
            {
                column.DefaultValueSql = (string)values.NewValue;
            }
        }
    }

    private void SetAnnotatedColumns(IEnumerable columns, string tableName)
    {
        foreach (var column in columns)
        {
            SetAnnotatedColumn(column, tableName);
        }
    }

    private string GetSqlDropConstraintQuery(string tableName, string columnName)
    {
        var tableNameSplitByDot = tableName.Split('.');
        var tableSchema = tableNameSplitByDot[0];
        var tablePureName = tableNameSplitByDot[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++;
        return str;
    }
}

30voto

calebboyd Points 2635

Les propriétés de votre modèle ne doivent pas nécessairement être des "propriétés automatiques", même si c'est plus facile. Et l'attribut DefaultValue est vraiment seulement une métadonnée informative. La réponse acceptée ici est une alternative à l'approche du constructeur.

public class Track
{

    private const int DEFAULT_LENGTH = 400;
    private int _length = DEFAULT_LENGTH;
    [DefaultValue(DEFAULT_LENGTH)]
    public int LongueurEnMètres {
        get { return _length; }
        set { _length = value; }
    }
}

vs.

public class Track
{
    public Track()
    {
        LongueurEnMètres = 400;   
    }

    public int LongueurEnMètres { get; set; }        
}

Cela ne fonctionnera que pour les applications créant et consommant des données en utilisant cette classe spécifique. Habituellement, ce n'est pas un problème si le code d'accès aux données est centralisé. Pour mettre à jour la valeur dans toutes les applications, vous devez configurer la source de données pour définir une valeur par défaut. La réponse de Devi ici montre comment cela peut être fait en utilisant des migrations, SQL, ou n'importe quel langage que votre source de données utilise.

17voto

Sameh Serag Points 1047

Ce que j'ai fait, j'ai initialisé les valeurs dans le constructeur de l'entité

Note : Les attributs DefaultValue ne définiront pas automatiquement les valeurs de vos propriétés, vous devez le faire vous-même

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X