Definindo o valor padrão de uma propriedade DateTime como DateTime.Now dentro do atributo de valor padrão System.ComponentModel


92

Alguém sabe como posso especificar o valor padrão para uma propriedade DateTime usando o atributo System.ComponentModel DefaultValue?

por exemplo, eu tento isso:

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

E espera que o valor seja uma expressão constante.

Isso ocorre no contexto do uso com Dados Dinâmicos do ASP.NET. Não quero fazer o scaffold da coluna DateCreated, mas simplesmente fornecer DateTime.Now se não estiver presente. Estou usando o Entity Framework como minha camada de dados

Felicidades,

Andrew

Respostas:


92

Você não pode fazer isso com um atributo porque eles são apenas metainformações geradas em tempo de compilação. Basta adicionar o código ao construtor para inicializar a data, se necessário, criar um gatilho e manipular os valores ausentes no banco de dados ou implementar o getter de forma que ele retorne DateTime.Now se o campo de apoio não for inicializado.

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

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;

2
Obrigado, antes de editar eu fui por outro caminho e interceptei o setter. Saudações pela ajuda :-)
REA_ANDREW

10
Uma getparte pode ser simplificada para:return dateCreated ?? DateTime.Now;
Vsevolod Krasnov

Esta solução pode ser um problema caso você queira suas aulas como POCO. No meu caso, minha classe deve ser tão clara quanto a classe POCO a ser transferida via WCF.
Jacob

1
@zHs Isso funcionou muito bem para mim usando o Database First com o Entity Framework.
Joshua K

1
Por favor, veja esta resposta se você veio aqui para um atributo.
Tempestade Muller

54

Adicione abaixo à propriedade DateTime

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

3
Isso é realmente o que eu estava procurando e deveria ser melhorado. Muitas das outras respostas adicionam a data atual ao inserir uma entidade, no entanto, isso não afeta o esquema (ou seja, torna getdate () o valor padrão). O problema para mim foi quando eu quis adicionar um registro diretamente na tabela (usando SQL) eu tive que fornecer a data (ou deixar NULL). Esta é uma solução muito melhor. Obrigado.
Tempestade Muller

8
.Computado é para ações de Adicionar e Atualizar. Use .Identity apenas para adicionar.
Renan Coelho

1
Melhor resposta de longe: deve ser adicionado que uma referência a System.ComponentModel.DataAnnotations.Schema deve ser adicionada para a anotação DatabaseGenerated
Yazan Khalaileh

Qual fuso horário será usado? Será UTC?
Almis

25

Não há motivo para eu sugerir que não seja possível fazer por meio de um atributo. Pode estar no backlog da Microsoft. Quem sabe.

A melhor solução que encontrei é usar o parâmetro defaultValueSql na primeira migração do código.

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

Eu não gosto da solução de referência frequente de configurá-lo no construtor de classe de entidade porque se qualquer coisa diferente do Entity Framework colocar um registro nessa tabela, o campo de data não obterá um valor padrão. E a ideia de usar um gatilho para lidar com esse caso parece errada para mim.


1
no caso de alterar e adicionar anulável use AddColumn ("dbo.table", "Created", c => c.DateTime (nullable: true, defaultValueSql: "getdate ()"));
Iman

Não é uma boa ideia, se você escolher seu banco de dados, você precisa refatorar este código.
termina

21

Eu testei isso no EF core 2.1

Aqui você não pode usar convenções ou anotações de dados. Você deve usar a 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 oficial


11

É possível e bastante simples:

para DateTime.MinValue

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

para qualquer outro valor como último argumento de DefaultValueAttributeespecificar string que representa o valor DateTime desejado.

Este valor deve ser uma expressão constante e é necessário para criar o objeto ( DateTime) usando TypeConverter.


Eu gostaria de acreditar que o uso funcionou para mim, mas infelizmente não funcionou. Resultado da migração; defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
Abdurrahman I.

5

Uma solução simples se você estiver usando o Entity Framework é adicionar uma classe particular e definir um construtor para a entidade, já que o framework não define um. Por exemplo, se você tiver uma entidade chamada Exemplo, você colocará o código a seguir em um arquivo separado.

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

Esta é de longe a solução mais elegante (e provavelmente também a pretendida) para o problema. Mas isso pressupõe que as pessoas estejam usando a EF Code First.

1
É assim que eu faço, até que percebi que adicionar uma propriedade complexa à minha entidade faz com que EF gere um construtor padrão, deixando-me sem nenhuma maneira de escrever o meu próprio ...
Dave Cousineau

5

Acho que a solução mais fácil é definir

Created DATETIME2 NOT NULL DEFAULT GETDATE()

na declaração de coluna e no VS2010 EntityModel designer definir propriedade de coluna correspondente StoreGeneratedPattern = Computed .


Se eu fizer assim e tornar a propriedade anulável, recebo um erro de validação EDMX, porque uma coluna não anulável é mapeada para uma propriedade anulável.
Niklas Peter

4

Criar uma nova classe de atributo é uma boa sugestão. No meu caso, eu queria especificar 'default (DateTime)' ou 'DateTime.MinValue' para que o serializador Newtonsoft.Json ignorasse os membros DateTime sem valores reais.

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

Sem o atributo DefaultValue, o serializador JSON produziria "1/1/0001 12:00:00 AM", embora a opção DefaultValueHandling.Ignore fosse definida.


4

Simplesmente considere definir seu valor no construtor de sua classe de entidade

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

}

Isso funcionou para mim. Embora eu tenha que definir a Datetimepropriedade nullable antes de fazer isso.
Ahashan Alam Sojib

3

Eu precisava de um carimbo de data / hora UTC como um valor padrão e, portanto, modifiquei a solução de Daniel assim:

    [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 o tutorial DateRangeAttribute, veja esta postagem de blog incrível


3

using System.ComponentModel.DataAnnotations.Schema;

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

1
Verifique como responder
Morse,

2

Há uma maneira. Adicione essas 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);
    }
}

E use DefaultDateTimeValue como um atributo para suas propriedades. Os valores a serem inseridos em seu atributo de validação são coisas como "Agora", que serão renderizados em tempo de execução a partir de uma instância DateTime criada com um ativador. O código-fonte é inspirado neste tópico: https://code.msdn.microsoft.com/A-flexible-Default-Value-11c2db19 . Eu mudei para fazer minha classe herdar com DefaultValueAttribute em vez de um ValidationAttribute.


2

Eu enfrentei o mesmo problema, mas o que funciona melhor para mim está abaixo:

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

1

Acabei de encontrar isso procurando por algo diferente, mas na nova versão C #, você pode usar uma versão ainda mais curta para isso:

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

Como você lida com isso no momento depende de qual modelo você está usando Linq to SQL ou EntityFramework?

No L2S você pode adicionar

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

        ExecuteDynamicInsert(instance);
    }
}

EF é um pouco mais complicado, consulte http://msdn.microsoft.com/en-us/library/cc716714.aspx para obter mais informações sobre a lógica de negócios da EF.


0

Sei que este post é um pouco antigo, mas tenho uma sugestão que pode ajudar um pouco.

Usei um Enum para determinar o que definir no construtor de atributo.

Declaração de propriedade:

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

Construtor de propriedade:

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

Acho que você pode fazer isso usando StoreGeneratedPattern = Identity(definido na janela de propriedades do designer de modelo).

Eu não teria imaginado que seria assim, mas ao tentar descobrir, percebi que algumas das minhas colunas de data já estavam padronizadas CURRENT_TIMESTAMP()e outras não. Verificando o modelo, vejo que a única diferença entre as duas colunas além do nome é que aquela que obtém o valor padrão foi StoreGeneratedPatterndefinida como Identity.

Eu não esperava que fosse esse o caminho, mas lendo a descrição, meio que faz sentido:

Determina se a coluna correspondente no banco de dados será gerada automaticamente durante as operações de inserção e atualização.

Além disso, embora isso faça com que a coluna do banco de dados tenha um valor padrão de "agora", acho que na verdade não define a propriedade como DateTime.Nowno POCO. Isso não tem sido um problema para mim, pois tenho um arquivo .tt personalizado que já define todas as minhas colunas de data para DateTime.Nowautomaticamente (na verdade não é difícil modificar o arquivo .tt por conta própria, especialmente se você tiver ReSharper e obter um destaque de sintaxe plugin. (As versões mais recentes do VS podem já destacar a sintaxe dos arquivos .tt, não tenho certeza.))

O problema para mim era: como faço para que a coluna do banco de dados tenha um padrão para que as consultas existentes que omitem essa coluna ainda funcionem? E a configuração acima funcionou para isso.

Ainda não testei, mas também é possível que a configuração interfira na configuração de seu próprio valor explícito. (Eu só tropecei nisso em primeiro lugar porque o EF6 Database First escreveu o modelo para mim dessa forma.)


0

No C # versão 6, é possível fornecer um valor padrão

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

1
Bem-vindo ao StackOverflow: se você postar código, XML ou amostras de dados, destaque essas linhas no editor de texto e clique no botão "amostras de código" ({}) na barra de ferramentas do editor ou usando Ctrl + K em seu teclado para formatar de maneira adequada e a sintaxe o realça!
WhatsThePoint

1
O mesmo que a resposta da Brandtware stackoverflow.com/a/47528230/52277
Michael Freidgeim

-1

Com EF 7:

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

script de migração:

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]

EF 7 não existe, uma coluna DateTime não pode ser identidade e a anotação não cria esse script de migração. E não é necessário alterar o script de migração manualmente, se é isso que você quer dizer. Existem anotações que adicionam o padrão, como já foi amplamente respondido aqui.
Gert Arnold

-5

Eu também queria isso e encontrei esta solução (estou usando apenas a parte da data - um horário padrão não faz sentido como um padrão PropertyGrid):

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

Isso apenas cria um novo atributo que você pode adicionar à sua propriedade DateTime. Por exemplo, se o padrão for DateTime.Now.Date:

[DefaultDate(0)]

31
AVISO!!!!!! isso é muito ruim e deve ser removido do SO. Passei horas depurando problemas causados ​​por essa sugestão. À primeira vista, parece bom e funciona. Mas isso é uma armadilha. Atributos exigem valores estáticos por um motivo. Eles são inicializados uma vez. Depois disso, o valor muda. Portanto, embora a primeira instância que você criar tenha uma boa aparência e as instâncias subsequentes tenham uma boa aparência, todas elas realmente usam o primeiro valor. Depois que seu aplicativo estiver em execução por algum tempo, você perceberá o problema e se perguntará por quê. E na depuração, quando você o executa por um minuto, ele funcionará bem. CUIDADO!
Chris Hynes,

3
Chris está certo. Eu encontrei o mesmo problema. Não use esta resposta .
sohtimsso1970,
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.