web-dev-qa-db-fra.com

Définition de la valeur par défaut d'une propriété DateTime sur DateTime.Now à l'intérieur de System.ComponentModel Valeur par défaut Attrbute

Est-ce que quelqu'un sait comment je peux spécifier la valeur par défaut d'une propriété DateTime à l'aide de l'attribut System.ComponentModel DefaultValue?

par exemple j'essaye ceci:

[DefaultValue(typeof(DateTime),DateTime.Now.ToString("yyyy-MM-dd"))]
public DateTime DateCreated { get; set; }

Et il s'attend à ce que la valeur soit une expression constante.

Ceci est dans le contexte d'utilisation avec ASP.NET Dynamic Data. Je ne veux pas échafauder la colonne DateCreated mais simplement fournir le DateTime.Now s'il n'est pas présent. J'utilise Entity Framework comme couche de données

À votre santé,

Andrew

84
REA_ANDREW

Vous ne pouvez pas faire cela avec un attribut car ce ne sont que des méta-informations générées au moment de la compilation. Ajoutez simplement du code au constructeur pour initialiser la date si nécessaire, créez un déclencheur et gérez les valeurs manquantes dans la base de données, ou implémentez le getter de manière à ce qu'il retourne DateTime.Now si le champ de sauvegarde n'est pas initialisé.

public DateTime DateCreated
{
   get
   {
      return this.dateCreated.HasValue
         ? this.dateCreated.Value
         : DateTime.Now;
   }

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;
89
Daniel Brückner

Ajouter ci-dessous à la propriété DateTime

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
21
user2660763

Je n'ai aucune raison de penser que cela ne devrait pas être possible avec un attribut. C'est peut-être dans le backlog de Microsoft. Qui sait.

La meilleure solution que j'ai trouvée consiste à utiliser le paramètre defaultValueSql dans la première migration du code.

CreateTable(
    "dbo.SomeTable",
    c => new
        {
            TheDateField = c.DateTime(defaultValueSql: "GETDATE()")
        });

Je n'aime pas la solution souvent référencée consistant à le définir dans le constructeur de la classe d'entité, car si autre chose que Entity Framework colle un enregistrement dans cette table, le champ de date n'obtiendra pas de valeur par défaut. Et l'idée d'utiliser un déclencheur pour gérer ce cas me semble tout simplement fausse.

20
d512

C'est possible et assez simple:

pour DateTime.MinValue

[System.ComponentModel.DefaultValue(typeof(DateTime), "")]

pour toute autre valeur comme dernier argument de DefaultValueAttribute, spécifiez une chaîne qui représente la valeur DateTime souhaitée.

Cette valeur doit être une expression constante et est nécessaire pour créer un objet (DateTime) à l'aide de TypeConverter.

10
DSW

J'ai testé cela sur EF core 2.1

Ici, vous ne pouvez utiliser ni conventions ni annotations de données. Vous devez utiliser l'API Fluent Fluent .

class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property(b => b.Created)
            .HasDefaultValueSql("getdate()");
    }
}

Document officiel

10
Sampath

Si vous utilisez Entity Framework, une solution simple consiste à ajouter une classe partielle et à définir un constructeur pour l'entité, ce dernier n'étant pas défini par celui-ci. Par exemple, si vous avez une entité nommée Example, vous devez placer le code suivant dans un fichier séparé.

namespace EntityExample
{
    public partial class Example : EntityObject
    {
        public Example()
        {
            // Initialize certain default values here.
            this._DateCreated = DateTime.Now;
        }
    }
}
6
Terence Golla

Je pense que la solution la plus simple est de définir

Created DATETIME2 NOT NULL DEFAULT GETDATE()

dans la déclaration de colonne et dans le concepteur EntityModel VS2010, définissez la propriété de colonne correspondante StoreGeneratedPattern = Calculé.

5
Robertas

Pensez simplement à définir sa valeur dans le constructeur de votre classe d'entité

public class Foo
{
       public DateTime DateCreated { get; set; }
       public Foo()
       {
           DateCreated = DateTime.Now;
       }

}
4
Muhammad Soliman

J'avais besoin d'un horodatage UTC comme valeur par défaut et j'ai donc modifié la solution de Daniel comme ceci:

    [Column(TypeName = "datetime2")]
    [XmlAttribute]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
    [Display(Name = "Date Modified")]
    [DateRange(Min = "1900-01-01", Max = "2999-12-31")]
    public DateTime DateModified {
        get { return dateModified; }
        set { dateModified = value; } 
    }
    private DateTime dateModified = DateTime.Now.ToUniversalTime();

Pour le tutoriel DateRangeAttribute, voir cet article de blog génial

3
Nuri

La création d'une nouvelle classe d'attributs est une bonne suggestion. Dans mon cas, je voulais spécifier 'default (DateTime)' ou 'DateTime.MinValue' pour que le sérialiseur Newtonsoft.Json ignore les membres DateTime sans valeurs réelles.

[JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore )]
[DefaultDateTime]
public DateTime EndTime;

public class DefaultDateTimeAttribute : DefaultValueAttribute
{
    public DefaultDateTimeAttribute()
        : base( default( DateTime ) ) { }

    public DefaultDateTimeAttribute( string dateTime )
        : base( DateTime.Parse( dateTime ) ) { }
}

Sans l'attribut DefaultValue, le sérialiseur JSON générerait "1/1/0001 12:00:00 AM" même si l'option DefaultValueHandling.Ignore était définie.

3
Erhhung

Il y a un moyen. Ajoutez ces classes:

DefaultDateTimeValueAttribute.cs

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Custom.Extensions;

namespace Custom.DefaultValueAttributes
{
    /// <summary>
    /// This class's DefaultValue attribute allows the programmer to use DateTime.Now as a default value for a property.
    /// Inspired from https://code.msdn.Microsoft.com/A-flexible-Default-Value-11c2db19. 
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class DefaultDateTimeValueAttribute : DefaultValueAttribute
    {
        public string DefaultValue { get; set; }
        private object _value;

        public override object Value
        {
            get
            {
                if (_value == null)
                    return _value = GetDefaultValue();

                return _value;
            }
        }

        /// <summary>
        /// Initialized a new instance of this class using the desired DateTime value. A string is expected, because the value must be generated at runtime.
        /// Example of value to pass: Now. This will return the current date and time as a default value. 
        /// Programmer tip: Even if the parameter is passed to the base class, it is not used at all. The property Value is overridden.
        /// </summary>
        /// <param name="defaultValue">Default value to render from an instance of <see cref="DateTime"/></param>
        public DefaultDateTimeValueAttribute(string defaultValue) : base(defaultValue)
        {
            DefaultValue = defaultValue;
        }

        public static DateTime GetDefaultValue(Type objectType, string propertyName)
        {
            var property = objectType.GetProperty(propertyName);
            var attribute = property.GetCustomAttributes(typeof(DefaultDateTimeValueAttribute), false)
                ?.Cast<DefaultDateTimeValueAttribute>()
                ?.FirstOrDefault();

            return attribute.GetDefaultValue();
        }

        private DateTime GetDefaultValue()
        {
            // Resolve a named property of DateTime, like "Now"
            if (this.IsProperty)
            {
                return GetPropertyValue();
            }

            // Resolve a named extension method of DateTime, like "LastOfMonth"
            if (this.IsExtensionMethod)
            {
                return GetExtensionMethodValue();
            }

            // Parse a relative date
            if (this.IsRelativeValue)
            {
                return GetRelativeValue();
            }

            // Parse an absolute date
            return GetAbsoluteValue();
        }

        private bool IsProperty
            => typeof(DateTime).GetProperties()
                .Select(p => p.Name).Contains(this.DefaultValue);

        private bool IsExtensionMethod
            => typeof(DefaultDateTimeValueAttribute).Assembly
                .GetType(typeof(DefaultDateTimeExtensions).FullName)
                .GetMethods()
                .Where(m => m.IsDefined(typeof(ExtensionAttribute), false))
                .Select(p => p.Name).Contains(this.DefaultValue);

        private bool IsRelativeValue
            => this.DefaultValue.Contains(":");

        private DateTime GetPropertyValue()
        {
            var instance = Activator.CreateInstance<DateTime>();
            var value = (DateTime)instance.GetType()
                .GetProperty(this.DefaultValue)
                .GetValue(instance);

            return value;
        }

        private DateTime GetExtensionMethodValue()
        {
            var instance = Activator.CreateInstance<DateTime>();
            var value = (DateTime)typeof(DefaultDateTimeValueAttribute).Assembly
                .GetType(typeof(DefaultDateTimeExtensions).FullName)
                .GetMethod(this.DefaultValue)
                .Invoke(instance, new object[] { DateTime.Now });

            return value;
        }

        private DateTime GetRelativeValue()
        {
            TimeSpan timeSpan;
            if (!TimeSpan.TryParse(this.DefaultValue, out timeSpan))
            {
                return default(DateTime);
            }

            return DateTime.Now.Add(timeSpan);
        }

        private DateTime GetAbsoluteValue()
        {
            DateTime value;
            if (!DateTime.TryParse(this.DefaultValue, out value))
            {
                return default(DateTime);
            }

            return value;
        }
    }
}

DefaultDateTimeExtensions.cs

using System;

namespace Custom.Extensions
{
    /// <summary>
    /// Inspired from https://code.msdn.Microsoft.com/A-flexible-Default-Value-11c2db19. See usage for more information.
    /// </summary>
    public static class DefaultDateTimeExtensions
    {
        public static DateTime FirstOfYear(this DateTime dateTime)
            => new DateTime(dateTime.Year, 1, 1, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond);

        public static DateTime LastOfYear(this DateTime dateTime)
            => new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond);

        public static DateTime FirstOfMonth(this DateTime dateTime)
            => new DateTime(dateTime.Year, dateTime.Month, 1, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond);

        public static DateTime LastOfMonth(this DateTime dateTime)
            => new DateTime(dateTime.Year, dateTime.Month, DateTime.DaysInMonth(dateTime.Year, dateTime.Month), dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond);
    }
}

Et utilisez DefaultDateTimeValue en tant qu'attribut de vos propriétés. Les valeurs à saisir pour votre attribut de validation sont des éléments tels que "Maintenant", qui seront restituées au moment de l'exécution à partir d'une instance DateTime créée avec un activateur. Le code source est inspiré de ce fil de discussion: https://code.msdn.Microsoft.com/A-flexible-Default-Value-11c2db19 . Je l'ai modifié pour que ma classe hérite avec DefaultValueAttribute au lieu d'un ValidationAttribute.

1
David

using System.ComponentModel.DataAnnotations.Schema;

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedOn { get; private set; }
1
Ali Kleit

Je sais que ce post est un peu vieux, mais a une suggestion qui peut aider certains.

J'ai utilisé un Enum pour déterminer ce qu'il faut définir dans le constructeur d'attributs.

Déclaration de propriété:

[DbProperty(initialValue: EInitialValue.DateTime_Now)]
public DateTime CreationDate { get; set; }

Constructeur de propriété:

Public Class DbProperty Inherits System.Attribute

    Public Property InitialValue As Object

    Public Sub New(ByVal initialValue As EInitialValue)
       Select Case initialValue
          Case EInitialValue.DateTime_Now
             Me.InitialValue = System.DateTime.Now

          Case EInitialValue.DateTime_Min
             Me.InitialValue = System.DateTime.MinValue

          Case EInitialValue.DateTime_Max
             Me.InitialValue = System.DateTime.MaxValue

       End Select

    End Sub
End Class

Enum:

Public Enum EInitialValue
   DateTime_Now
   DateTime_Min
   DateTime_Max
End Enum
0
vincenormand

Je viens de trouver cela à la recherche de quelque chose de différent, mais dans la nouvelle version C #, vous pouvez utiliser une version encore plus courte pour cela:

public DateTime DateCreated { get; set; } = DateTime.Now;
0
Brandtware

La façon dont vous gérez cela à l’heure actuelle dépend du modèle que vous utilisez, Linq to SQL ou EntityFramework?

Dans L2S, vous pouvez ajouter

public partial class NWDataContext
{
    partial void InsertCategory(Category instance)
    {
        if(Instance.Date == null)
            Instance.Data = DateTime.Now;

        ExecuteDynamicInsert(instance);
    }
}

EF est un peu plus compliqué, voyez http://msdn.Microsoft.com/en-us/library/cc716714.aspx pour plus d’informations sur la logique de travail EF.

0
Wizzard

Je pensez vous pouvez le faire en utilisant StoreGeneratedPattern = Identity (Défini dans la fenêtre des propriétés du concepteur de modèle).

Je n'aurais pas deviné que ce serait la meilleure façon de le faire, mais en essayant de le comprendre, j'ai remarqué que certaines de mes colonnes de date avaient déjà la valeur par défaut de CURRENT_TIMESTAMP() et que d'autres ne l'étaient pas. En vérifiant le modèle, je constate que la seule différence entre les deux colonnes, à part le nom, est que celle qui obtient la valeur par défaut a StoreGeneratedPattern défini sur Identity.

Je ne m'attendais pas à ce que ce soit le chemin, mais lire la description a du sens:

Détermine si la colonne correspondante de la base de données sera générée automatiquement lors des opérations d'insertion et de mise à jour.

De plus, bien que cela donne à la colonne de base de données une valeur par défaut de "maintenant", je suppose que cela ne définit pas réellement la propriété comme étant DateTime.Now Dans le POCO. Ce n’est pas un problème pour moi car j’ai un fichier .tt personnalisé qui définit déjà automatiquement toutes mes colonnes de date sur DateTime.Now (Il n’est pas difficile de modifier le fichier .tt vous-même, surtout si vous avez ReSharper et obtenez un plugin de coloration syntaxique.

Le problème pour moi était: comment obtenir une valeur par défaut dans la colonne de la base de données pour que les requêtes existantes qui omettent cette colonne continuent de fonctionner? Et le réglage ci-dessus a fonctionné pour cela.

Je ne l'ai pas encore testé, mais il est également possible que cela interfère avec la définition de votre propre valeur explicite. (Je ne suis tombé sur cette question que parce que EF6 Database First a écrit le modèle pour moi de cette façon.)

0
Dave Cousineau
public DateTime DateCreated
{
   get
   {
      return (this.dateCreated == default(DateTime))
         ? this.dateCreated = DateTime.Now
         : this.dateCreated;
   }

   set { this.dateCreated = value; }
}
private DateTime dateCreated = default(DateTime);
0
bobac