web-dev-qa-db-fra.com

Entity Framework 6 Code first Valeur par défaut

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

Je vous remercie.

167
marino-krk

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

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

Cela faisait longtemps, mais je laissais une note aux autres ... J'ai réalisé ce qu'il fallait avec un attribut et j'ai décoré les champs de ma classe de modèle avec cet attribut comme je le souhaitais.

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

Vous avez l'aide de ces 2 articles:

Ce que j'ai fait:

Définir un 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 SqlGenerator personnalisé

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

Ensuite, dans le constructeur Configuration de la migration, enregistrez le générateur SQL personnalisé.

SetSqlGenerator("System.Data.SqlClient", new HarmonyMigrationSqlGenerator());
61
ravinsp

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 Valeur par défaut, la contrainte sur la colonne de la base de données ne sera pas supprimée. La valeur par défaut précédente reste donc dans la base de données.

Voici une solution complète au problème, y compris la suppression des contraintes SQL sur la suppression d'attribut . Je réutilise également l'attribut DefaultValue natif de .NET Framework.

Usage

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

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

Fichier IdentityModels.cs

Ajouter/mettre à jour cette méthode dans votre classe 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);
}

Fichier configuration.cs

Mettez à jour votre constructeur de classe Configuration en enregistrant le générateur Sql personnalisé, comme suit:

internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
    public Configuration()
    {
        // DefaultValue Sql Generator
        SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
    }
}

Ajoutez ensuite une classe de générateur SQL personnalisée (vous pouvez l’ajouter au fichier Configuration.cs ou à un fichier séparé).

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

Les propriétés de votre modèle ne doivent pas nécessairement être des "propriétés automatiques", bien que cela soit plus facile. Et l'attribut DefaultValue n'est en réalité que des métadonnées informatives. La réponse acceptée ici est une alternative à l'approche constructeur.

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

vs.

public class Track
{
    public Track()
    {
        LengthInMeters = 400;   
    }

    public int LengthInMeters { get; set; }        
}

Cela ne fonctionnera que pour les applications créant et consommant des données à l'aide de cette classe spécifique. Généralement, 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 afin de définir une valeur par défaut. La réponse de Devi montre comment procéder à l'aide de migrations, de sql ou de la langue parlée par votre source de données.

24
calebboyd

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

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

8
Sameh Deabes

Après le commentaire de @SedatKapanoglu, j'ajoute toute mon approche qui fonctionne, car il avait raison, le simple fait d'utiliser l'API fluide ne fonctionne pas.

1- Créez un générateur de code personnalisé et remplacez Generate pour un 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- Affecter le nouveau générateur de code:

public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
    public Configuration()
    {
        CodeGenerator = new ExtendedMigrationCodeGenerator();
        AutomaticMigrationsEnabled = false;
    }
}

3- Utilisez des API fluides pour créer l'annotation:

public static void Configure(DbModelBuilder builder){    
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);            
}
6
Denny Puig

J'admets que mon approche échappe à toute l'approche du "Code First". Mais si vous avez la possibilité de changer la valeur par défaut dans la table elle-même ... C'est beaucoup plus simple que les longueurs que vous devez parcourir plus haut ... Je suis trop paresseux pour faire tout ce travail! 

Il semble presque que l'idée originale des affiches fonctionne:

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

Je pensais qu'ils venaient de commettre l'erreur d'ajouter des guillemets ... mais hélas pas d'intuition. Les autres suggestions étaient tout simplement trop pour moi (étant donné que j’ai les privilèges nécessaires pour entrer dans la table et faire les changements ... là où peu de développeurs le voudront dans toutes les situations). En fin de compte, je l'ai fait à l'ancienne. J'ai défini la valeur par défaut dans la table SQL Server ... Je veux dire vraiment, déjà assez! NOTE: J'ai testé plus avant de faire une base de données add-migration et update et les modifications restaient bloquées. enter image description here

2
Anthony Griggs

Il suffit de surcharger le constructeur par défaut de la classe Model et de transmettre tout paramètre pertinent que vous pouvez utiliser ou non. Grâce à cela, vous pouvez facilement fournir des valeurs par défaut pour les attributs. Ci-dessous un exemple.

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

}
1
Bappa Malakar

C'est simple! Juste annoter avec requis.

[Required]
public bool MyField { get; set; }

la migration résultante sera:

migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);

Si vous voulez true, remplacez defaultValue par true dans la migration avant de mettre à jour la base de données.

0
Marisol Gutiérrez