Impostazione del valore predefinito di una proprietà DateTime su DateTime.Now all'interno di System.ComponentModel Valore predefinito Attrbute


95

Qualcuno sa come posso specificare il valore predefinito per una proprietà DateTime utilizzando l'attributo System.ComponentModel DefaultValue?

per esempio provo questo:

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

E si aspetta che il valore sia un'espressione costante.

Questo è nel contesto dell'utilizzo con ASP.NET Dynamic Data. Non voglio impalcare la colonna DateCreated ma fornire semplicemente DateTime.Now se non è presente. Utilizzo Entity Framework come livello dati

Saluti,

Andrea

Risposte:


94

Non puoi farlo con un attributo perché sono solo meta informazioni generate in fase di compilazione. Basta aggiungere il codice al costruttore per inizializzare la data se necessario, creare un trigger e gestire i valori mancanti nel database o implementare il getter in modo che restituisca DateTime.Now se il campo di supporto non è inizializzato.

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

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;

2
Grazie, prima di modificare sono andato dall'altra parte e ho intercettato il setter. Grazie per l'aiuto :-)
REA_ANDREW

10
Una getparte può essere semplificata in:return dateCreated ?? DateTime.Now;
Vsevolod Krasnov

Questa soluzione potrebbe essere un problema nel caso in cui desideri le tue classi come POCO. Nel mio caso, la mia classe dovrebbe essere chiara come la classe POCO da trasferire tramite WCF.
Jacob il

1
@zHs Questo ha funzionato alla grande per me utilizzando Database First con Entity Framework.
Joshua K

1
Per favore guarda questa risposta se sei venuto qui per un attributo.
Storm Muller

55

Aggiungi di seguito alla proprietà DateTime

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

3
Questo è effettivamente quello che stavo cercando e dovrebbe salire più in alto. Molte delle altre risposte aggiungono la data corrente quando si inserisce un'entità, tuttavia non influisce sullo schema (ad esempio, rendere getdate () il valore predefinito). Il problema per me era che quando volevo aggiungere un record direttamente nella tabella (usando SQL) dovevo fornire la data (o lasciarla NULL). Questa è una soluzione molto migliore. Grazie.
Storm Muller

8
.Computed è per le azioni Aggiungi e Aggiorna. Usa .Identity solo per Aggiungi.
Renan Coelho

1
Migliore risposta di gran lunga: va aggiunto che un riferimento a System.ComponentModel.DataAnnotations.Schema dovrebbe essere aggiunto per l'annotazione DatabaseGenerated
Yazan Khalaileh

Quale fuso orario verrà utilizzato? Sarà UTC?
Almis

25

Non c'è motivo per cui riesco a pensare che non dovrebbe essere possibile farlo tramite un attributo. Potrebbe essere nel backlog di Microsoft. Chissà.

La soluzione migliore che ho trovato è utilizzare il parametro defaultValueSql nella prima migrazione del codice.

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

Non mi piace la soluzione di riferimento spesso di impostarlo nel costruttore della classe di entità perché se qualcosa di diverso da Entity Framework inserisce un record in quella tabella, il campo della data non otterrà un valore predefinito. E l'idea di utilizzare un trigger per gestire quel caso mi sembra sbagliata.


1
in caso di modifica e aggiunta di nullable, utilizzare AddColumn ("dbo.table", "Created", c => c.DateTime (nullable: true, defaultValueSql: "getdate ()"));
Iman

Non è una buona idea, se scegli il tuo db, devi eseguire il refactoring di questo codice.
posa l'

22

L'ho testato su EF core 2.1

Qui non è possibile utilizzare né convenzioni né annotazioni di dati. Devi utilizzare l' API 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()");
    }
}

Doc. Ufficiale


12

È possibile e abbastanza semplice:

per DateTime.MinValue

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

per qualsiasi altro valore come ultimo argomento della DefaultValueAttributestringa di specifica che rappresenta il valore DateTime desiderato.

Questo valore deve essere un'espressione costante ed è richiesto per creare object ( DateTime) utilizzando TypeConverter.


Mi piacerebbe credere che l'utilizzo funzioni per me, ma sfortunatamente non è stato così. Risultato della migrazione; defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
Abdurrahman I.

Ho appena provato a inserire una stringa data ("1/1/20") invece di una stringa vuota e lo converte correttamente.
user890332

5

Una soluzione semplice se si utilizza Entity Framework è l'aggiunta di una classe partical e la definizione di un costruttore per l'entità poiché il framework non ne definisce uno. Ad esempio, se si dispone di un'entità denominata Example, si inserirà il codice seguente in un file separato.

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

Questa è di gran lunga la soluzione più elegante (e probabilmente anche quella prevista) al problema. Ma questo presuppone che le persone utilizzino EF Code First.

1
Questo è come lo faccio, finché non mi sono reso conto che l'aggiunta di una proprietà complessa alla mia entità fa sì che EF generi un costruttore predefinito, lasciandomi senza modo di scrivere il mio ...
Dave Cousineau

5

Penso che la soluzione più semplice sia impostare

Created DATETIME2 NOT NULL DEFAULT GETDATE()

nella dichiarazione di colonna e nella finestra di progettazione EntityModel VS2010 impostare la proprietà di colonna corrispondente StoreGeneratedPattern = Computed .


Se lo faccio in questo modo e rendo la proprietà nullable, ottengo un errore di convalida EDMX, perché una colonna non nullable viene mappata a una proprietà nullable.
Niklas Peter

4

La creazione di una nuova classe di attributi è un buon suggerimento. Nel mio caso, volevo specificare "default (DateTime)" o "DateTime.MinValue" in modo che il serializzatore Newtonsoft.Json ignorasse i membri DateTime senza valori reali.

[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 ) ) { }
}

Senza l'attributo DefaultValue, il serializzatore JSON restituirebbe "1/1/0001 12:00:00 AM" anche se l'opzione DefaultValueHandling.Ignore era impostata.


4

Considera semplicemente l'impostazione del suo valore nel costruttore della tua classe di entità

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

}

Questo ha funzionato per me. Anche se ho dovuto impostare la Datetimeproprietà nullable prima di farlo.
Ahashan Alam Sojib,

3

Avevo bisogno di un timestamp UTC come valore predefinito e quindi ho modificato la soluzione di Daniel in questo modo:

    [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();

Per il tutorial su DateRangeAttribute, guarda questo fantastico post sul blog


3

using System.ComponentModel.DataAnnotations.Schema;

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

1
Si prega di controllare come rispondere
Morse

2

C'è un modo. Aggiungi queste classi:

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

E usa DefaultDateTimeValue come attributo per le tue proprietà. I valori da inserire nell'attributo di convalida sono cose come "Now", che verrà visualizzato in fase di esecuzione da un'istanza DateTime creata con un attivatore. Il codice sorgente è ispirato da questo thread: https://code.msdn.microsoft.com/A-forrect-Default-Value-11c2db19 . L'ho cambiato per fare in modo che la mia classe erediti con DefaultValueAttribute invece di ValidationAttribute.


2

Ho affrontato lo stesso problema, ma quello che funziona meglio per me è di seguito:

public DateTime CreatedOn { get; set; } = DateTime.Now;

1

Ho appena trovato questo cercando qualcosa di diverso, ma nella nuova versione C #, puoi usare una versione ancora più breve per quello:

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

0
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

Il modo in cui gestisci questo al momento dipende da quale modello stai usando Linq to SQL o EntityFramework?

In L2S puoi aggiungere

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

        ExecuteDynamicInsert(instance);
    }
}

EF è un po 'più complicato, vedere http://msdn.microsoft.com/en-us/library/cc716714.aspx per maggiori informazioni sulla logica di business EF.


0

So che questo post è un po 'vecchio, ma ho un suggerimento che potrebbe aiutare alcuni.

Ho usato un Enum per determinare cosa impostare nel costruttore di attributi.

Dichiarazione di proprietà:

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

Costruttore di proprietà:

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

Penso che tu possa farlo usando StoreGeneratedPattern = Identity(impostato nella finestra delle proprietà del designer del modello).

Non avrei mai immaginato che sarebbe stato come farlo, ma mentre cercavo di capirlo ho notato che alcune delle mie colonne di date erano già predefinite CURRENT_TIMESTAMP()e altre no. Controllando il modello, vedo che l'unica differenza tra le due colonne oltre al nome è che quella che ottiene il valore predefinito è StoreGeneratedPatternimpostata su Identity.

Non mi sarei aspettato che fosse così, ma leggendo la descrizione ha un senso:

Determina se la colonna corrispondente nel database verrà generata automaticamente durante le operazioni di inserimento e aggiornamento.

Inoltre, anche se questo fa sì che la colonna del database abbia un valore predefinito di "ora", immagino che non imposti effettivamente la proprietà in modo che sia DateTime.Nownel POCO. Questo non è stato un problema per me poiché ho un file .tt personalizzato che imposta già automaticamente tutte le colonne della mia data DateTime.Now(in realtà non è difficile modificare il file .tt da solo, specialmente se hai ReSharper e ottieni un'evidenziazione della sintassi plugin (le versioni più recenti di VS potrebbero già evidenziare i file .tt, non sono sicuro)).

Il problema per me era: come posso fare in modo che la colonna del database abbia un valore predefinito in modo che le query esistenti che omettono quella colonna continuino a funzionare? E l'impostazione di cui sopra ha funzionato per questo.

Non l'ho ancora testato, ma è anche possibile che l'impostazione di questo interferisca con l'impostazione del tuo valore esplicito. (Mi sono imbattuto in questo solo in primo luogo perché EF6 Database First ha scritto il modello per me in questo modo.)


0

Nella versione 6 di C # è possibile fornire un valore predefinito

public DateTime fieldname { get; set; } = DateTime.Now;

1
Benvenuto in StackOverflow: se inserisci codice, XML o campioni di dati, evidenzia queste righe nell'editor di testo e fai clic sul pulsante "Esempi di codice" ({}) sulla barra degli strumenti dell'editor o utilizza Ctrl + K sulla tastiera per formattare correttamente e la sintassi lo evidenzia!
WhatsThePoint

1
La stessa risposta di Brandtware stackoverflow.com/a/47528230/52277
Michael Freidgeim

-1

Con EF 7:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
Column(TypeName = "datetime2")]
DateTime? Dateadded { get; set; }

script di migrazione:

AlterColumn("myschema.mytable", "Dateadded", c => c.DateTime(nullable: false, precision: 7, storeType: "datetime2", defaultValueSql: "getutcdate()"));

risultato:

ALTER TABLE [MySchema].[MyTable] ADD  CONSTRAINT [DF_MySchema.MyTable_Dateadded]  DEFAULT (getutcdate()) FOR [Dateadded]

EF 7 non esiste, una colonna DateTime non può essere identità e l'annotazione non crea lo script di migrazione. E non è necessario modificare manualmente lo script di migrazione, se è questo che intendi. Ci sono annotazioni che aggiungono l'impostazione predefinita, come già ampiamente risposto qui.
Gert Arnold

-5

Volevo anche questo e ho trovato questa soluzione (sto usando solo la parte della data - un tempo predefinito non ha senso come valore predefinito di PropertyGrid):

public class DefaultDateAttribute : DefaultValueAttribute {
  public DefaultDateAttribute(short yearoffset)
    : base(DateTime.Now.AddYears(yearoffset).Date) {
  }
}

Questo crea solo un nuovo attributo che puoi aggiungere alla tua proprietà DateTime. Ad esempio, se il valore predefinito è DateTime.Now.Date:

[DefaultDate(0)]

32
AVVERTIMENTO!!!!!! questo è molto negativo e dovrebbe essere rimosso da SO. Ho appena passato ore a eseguire il debug dei problemi causati da questo suggerimento. A prima vista sembra buono e sembra funzionare. Ma questa è una trappola. Gli attributi richiedono valori statici per un motivo. Vengono inizializzati una volta. Dopodiché, il valore cambia. Quindi, sebbene la prima istanza che crei sembrerà a posto e le istanze successive sembreranno a posto, in realtà tutte usano il primo valore. Quando la tua app è in esecuzione da un po ', noterai il problema e ti chiederai perché. E nel debug, quando lo esegui per un minuto, funzionerà bene. ATTENZIONE!
Chris Hynes

3
Chris ha ragione. Ho riscontrato lo stesso problema. Non usare questa risposta .
sohtimsso1970
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.