Establecer el valor predeterminado de una propiedad DateTime en DateTime.Now dentro del atributo de valor predeterminado System.ComponentModel

95

¿Alguien sabe cómo puedo especificar el valor predeterminado para una propiedad DateTime usando el atributo System.ComponentModel DefaultValue?

por ejemplo, intento esto:

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

Y espera que el valor sea una expresión constante.

Esto está en el contexto del uso con ASP.NET Dynamic Data. No quiero crear un andamio en la columna DateCreated, sino simplemente proporcionar DateTime.Now si no está presente. Estoy usando Entity Framework como mi capa de datos

Salud,

Andrés

REA_ANDREW
fuente

Respuestas:

94

No puede hacer esto con un atributo porque son solo metainformaciones generadas en el momento de la compilación. Simplemente agregue código al constructor para inicializar la fecha si es necesario, cree un disparador y maneje los valores faltantes en la base de datos, o implemente el captador de manera que devuelva DateTime.Now si el campo de respaldo no está inicializado.

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

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;
Daniel Brückner
fuente
2
Gracias, antes de editar fui por el otro lado e intercepté al colocador. Saludos por la ayuda :-)
REA_ANDREW
10
Una getparte se puede simplificar a:return dateCreated ?? DateTime.Now;
Vsevolod Krasnov
Esta solución puede ser un problema en caso de que desee sus clases como POCO. En mi caso, mi clase debería ser tan clara como la clase POCO para ser transferida a través de WCF.
Jacob
1
@zHs Esto funcionó muy bien para mí al usar Database First con Entity Framework.
Joshua K
1
Consulte esta respuesta si vino aquí por un atributo.
Storm Muller
55

Agregue a continuación a la propiedad DateTime

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
usuario2660763
fuente
3
Esto es realmente lo que estaba buscando y debería llegar más arriba. Muchas de las otras respuestas agregan la fecha actual al insertar una entidad, sin embargo, no afecta el esquema (es decir, hace que getdate () sea el valor predeterminado). El problema para mí fue que cuando quería agregar un registro directamente en la tabla (usando SQL) tenía que proporcionar la fecha (o dejarla como NULL). Esta es una solución mucho mejor. Gracias.
Storm Muller
8
.Computed es para acciones de Agregar y Actualizar. Utilice .Identity solo para Agregar.
Renan Coelho
1
La mejor respuesta con diferencia: se debe agregar que se debe agregar una referencia a System.ComponentModel.DataAnnotations.Schema para la anotación DatabaseGenerated
Yazan Khalaileh
¿Qué zona horaria se utilizará? ¿Será UTC?
Almis
25

No hay ninguna razón por la que pueda pensar que no debería ser posible hacerlo a través de un atributo. Podría estar en la cartera de pedidos de Microsoft. Quién sabe.

La mejor solución que he encontrado es usar el parámetro defaultValueSql en la primera migración del código.

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

No me gusta la solución de referencia frecuente de configurarlo en el constructor de la clase de entidad porque si algo más que Entity Framework pega un registro en esa tabla, el campo de fecha no obtendrá un valor predeterminado. Y la idea de usar un disparador para manejar ese caso me parece incorrecta.

d512
fuente
1
en caso de alterar y agregar que admite valores NULL, use AddColumn ("dbo.table", "Created", c => c.DateTime (nullable: true, defaultValueSql: "getdate ()"));
Iman
No es una buena idea, si elige su base de datos, necesita refactorizar este código.
sale el
22

He probado esto en EF core 2.1

Aquí no puede utilizar convenciones ni anotaciones de datos. Debe utilizar la 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()");
    }
}

Documento oficial

Sampath
fuente
12

Es posible y bastante simple:

para DateTime.MinValue

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

para cualquier otro valor como último argumento de la DefaultValueAttributecadena especificada que representa el valor DateTime deseado.

Este valor debe ser una expresión constante y es necesario para crear object ( DateTime) usando TypeConverter.

DSW
fuente
Me gustaría creer que el uso funciona para mí, pero desafortunadamente no fue así. Resultado de la migración; defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
Abdurrahman I.
Intenté poner una fecha de cadena ("1/1/20") en lugar de una cadena vacía y la convierte correctamente.
user890332
5

Una solución simple si está utilizando Entity Framework es agregar una clase particular y definir un constructor para la entidad, ya que el marco no define uno. Por ejemplo, si tiene una entidad llamada Example, colocaría el siguiente código en un archivo separado.

namespace EntityExample
{
    public partial class Example : EntityObject
    {
        public Example()
        {
            // Initialize certain default values here.
            this._DateCreated = DateTime.Now;
        }
    }
}
Terence Golla
fuente
Esta es, con mucho, la solución más elegante (y probablemente también la prevista) al problema. Pero esto supone que las personas estén usando EF Code First.
1
Así es como lo hago, hasta que me di cuenta de que agregar una propiedad compleja a mi entidad hace que EF genere un constructor predeterminado, dejándome sin forma de escribir el mío ...
Dave Cousineau
5

Creo que la solución más sencilla es configurar

Created DATETIME2 NOT NULL DEFAULT GETDATE()

en la declaración de columna y en VS2010 EntityModel, el diseñador establece la propiedad de columna correspondiente StoreGeneratedPattern = Computed .

Robertas
fuente
Si lo hago así y hago que la propiedad sea anulable, obtengo un error de validación de EDMX, porque una columna no anulable se asigna a una propiedad anulable.
Niklas Peter
4

Crear una nueva clase de atributo es una buena sugerencia. En mi caso, quería especificar 'predeterminado (DateTime)' o 'DateTime.MinValue' para que el serializador Newtonsoft.Json ignorara los miembros de DateTime sin valores reales.

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

Sin el atributo DefaultValue, el serializador JSON daría como resultado "1/1/0001 12:00:00 AM" aunque la opción DefaultValueHandling.Ignore estuviera configurada.

Erhhung
fuente
4

Simplemente considere establecer su valor en el constructor de su clase de entidad

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

}
Muhammad Soliman
fuente
Esto funcionó para mí. Aunque tuve que establecer la Datetimepropiedad anulable antes de hacer esto.
Ahashan Alam Sojib
3

Necesitaba una marca de tiempo UTC como valor predeterminado y, por lo tanto, modifiqué la solución de Daniel de esta manera:

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

Para el tutorial de DateRangeAttribute, vea esta increíble publicación de blog

Nuri
fuente
3

utilizando System.ComponentModel.DataAnnotations.Schema;

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedOn { get; private set; }
Ali Kleit
fuente
1
Compruebe cómo responder
Morse
2

Hay una manera. Agrega estas clases:

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

Y utilice DefaultDateTimeValue como atributo de sus propiedades. El valor para ingresar a su atributo de validación son cosas como "Ahora", que se representará en tiempo de ejecución desde una instancia de DateTime creada con un Activador. El código fuente está inspirado en este hilo: https://code.msdn.microsoft.com/A-flexible-Default-Value-11c2db19 . Lo cambié para que mi clase herede con DefaultValueAttribute en lugar de ValidationAttribute.

David
fuente
2

Enfrenté el mismo problema, pero el que mejor funciona para mí es el siguiente:

public DateTime CreatedOn { get; set; } = DateTime.Now;
usuario3675084
fuente
1

Acabo de encontrar esto buscando algo diferente, pero en la nueva versión de C #, puede usar una versión aún más corta para eso:

public DateTime DateCreated { get; set; } = DateTime.Now;
Brandtware
fuente
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);
bobac
fuente
0

¿Cómo maneja esto en este momento depende del modelo que esté usando Linq to SQL o EntityFramework?

En L2S puede agregar

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

        ExecuteDynamicInsert(instance);
    }
}

EF es un poco más complicado, consulte http://msdn.microsoft.com/en-us/library/cc716714.aspx para obtener más información sobre la lógica empresarial de EF.

Wizzard
fuente
0

Sé que esta publicación es un poco antigua, pero tengo una sugerencia que puede ayudar a algunos.

Usé un Enum para determinar qué establecer en el constructor de atributos.

Declaración de propiedad:

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

Constructor de propiedad:

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
Vincenormand
fuente
0

Yo creo que puede hacer esto utilizando StoreGeneratedPattern = Identity(ajustado en la ventana de propiedades de diseño del modelo).

No habría adivinado que sería así, pero mientras trataba de averiguarlo, noté que algunas de mis columnas de fecha ya estaban predeterminadas CURRENT_TIMESTAMP()y otras no. Al verificar el modelo, veo que la única diferencia entre las dos columnas además del nombre es que la que obtiene el valor predeterminado se ha StoreGeneratedPatternestablecido enIdentity .

No hubiera esperado que fuera así, pero al leer la descripción, tiene sentido:

Determina si la columna correspondiente en la base de datos se generará automáticamente durante las operaciones de inserción y actualización.

Además, aunque esto hace que la columna de la base de datos tenga un valor predeterminado de "ahora", supongo que en realidad no establece que la propiedad esté DateTime.Nowen el POCO. Esto no ha sido un problema para mí, ya que tengo un archivo .tt personalizado que ya establece todas mis columnas de fecha enDateTime.Now automáticamente (en realidad, no es difícil modificar el archivo .tt usted mismo, especialmente si tiene ReSharper y obtiene un resaltado de sintaxis plugin. (Las versiones más nuevas de VS ya pueden resaltar la sintaxis de los archivos .tt, no estoy seguro))

El problema para mí fue: ¿cómo puedo hacer que la columna de la base de datos tenga un valor predeterminado para que las consultas existentes que omiten esa columna sigan funcionando? Y la configuración anterior funcionó para eso.

No lo he probado todavía, pero también es posible que configurar esto interfiera con la configuración de su propio valor explícito. (Solo me topé con esto en primer lugar porque EF6 Database First escribió el modelo para mí de esta manera).

Dave Cousineau
fuente
0

En C # Versión 6, es posible proporcionar un valor predeterminado

public DateTime fieldname { get; set; } = DateTime.Now;
codificación feliz
fuente
1
Bienvenido a StackOverflow: si publica muestras de código, XML o datos, resalte esas líneas en el editor de texto y haga clic en el botón "muestras de código" ({}) en la barra de herramientas del editor o use Ctrl + K en su teclado para formatear bien y la sintaxis lo resalta!
WhatsThePoint
1
Lo mismo que la respuesta de Brandtware stackoverflow.com/a/47528230/52277
Michael Freidgeim
-1

Con EF 7:

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

script de migración:

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

resultado:

ALTER TABLE [MySchema].[MyTable] ADD  CONSTRAINT [DF_MySchema.MyTable_Dateadded]  DEFAULT (getutcdate()) FOR [Dateadded]
Justin
fuente
EF 7 no existe, una columna DateTime no puede ser identidad y la anotación no crea ese script de migración. Y no es necesario modificar el script de migración manualmente, si eso es lo que quiere decir. Hay anotaciones que agregan el predeterminado, como ya se respondió ampliamente aquí.
Gert Arnold
-5

También quería esto y se me ocurrió esta solución (solo estoy usando la parte de la fecha; un tiempo predeterminado no tiene sentido como predeterminado de PropertyGrid):

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

Esto solo crea un nuevo atributo que puede agregar a su propiedad DateTime. Por ejemplo, si el valor predeterminado es DateTime.Now.Date:

[DefaultDate(0)]
Michel Smits
fuente
32
¡¡¡¡¡¡ADVERTENCIA!!!!!! esto es muy malo y debería eliminarse de SO. Acabo de pasar horas depurando problemas causados ​​por esta sugerencia. A primera vista, se ve bien y parece funcionar. Pero esta es una trampa. Los atributos requieren valores estáticos por una razón. Se inicializan una vez. Después de eso, el valor cambia. Entonces, si bien la primera instancia que cree se verá bien y las instancias posteriores se verán bien, todas usan el primer valor. Una vez que su aplicación se haya ejecutado durante un tiempo, notará el problema y se preguntará por qué. Y en la depuración, cuando lo ejecutes por un minuto, funcionará bien. ¡TENER CUIDADO!
Chris Hynes
3
Chris tiene razón. Encontré el mismo problema. No use esta respuesta .
sohtimsso1970