在System.ComponentModel默认值Attrbute中将DateTime属性的默认值设置为DateTime.Now


94

有谁知道如何使用System.ComponentModel DefaultValue属性为DateTime属性指定Default值?

例如,我尝试这样:

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

并且它期望该值是一个常量表达式。

这是在与ASP.NET动态数据一起使用的上下文中。我不想设置DateCreated列,而只是提供DateTime.Now(如果不存在)。我正在使用实体框架作为数据层

干杯,

安德鲁

Answers:


93

您不能使用属性来执行此操作,因为它们只是在编译时生成的元信息。只需将代码添加到构造函数中即可根据需要初始化日期,创建触发器并处理数据库中的缺失值,或者以返回DateTime的方式实现getter。如果未初始化后备字段,则立即返回。

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

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;

2
谢谢,在您进行编辑之前,我采取了另一种方法,并拦截了塞特犬。为帮助欢呼:-)
REA_ANDREW,2009年

10
get:部分可以简化为return dateCreated ?? DateTime.Now;
梅耶克拉斯诺夫

如果您希望将课程设为POCO,则此解决方案可能会出现问题。就我而言,我的班级应该和要通过WCF传输的POCO班级一样清晰。
雅各布

1
@zHs这对将Database First和Entity Framework一起使用对我来说非常有用。
Joshua K

1
如果您是来这里查询属性的,请参见此答案
Storm Muller

55

在下面添加到DateTime属性

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

3
这实际上是我一直在寻找的东西,应该会越来越高。许多其他答案的确会在插入实体时添加当前日期,但是不会影响架构(即,使getdate()为默认值)。对我来说,问题是当我想将记录直接添加到表中(使用SQL)时,我必须提供日期(或将其保留为NULL)。这是一个更好的解决方案。谢谢。
Storm Muller

8
.Computed用于添加和更新操作。将.Identity仅用于添加。
雷南·科埃略

1
到目前为止最好的答案:应该添加对DatabaseGenerated注释的System.ComponentModel.DataAnnotations.Schema引用
Yazan Khalaileh,

将使用哪个时区?会是UTC吗?
Almis

25

我没有理由不能通过属性做到这一点。它可能在Microsoft的积压中。谁知道。

我发现的最佳解决方案是在代码首次迁移中使用defaultValueSql参数。

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

我不喜欢在实体类构造函数中设置它的经常参考的解决方案,因为如果除Entity Framework之外的其他任何东西在该表中保留一条记录,则date字段将不会获得默认值。使用触发器来处理这种情况的想法对我来说似乎是错误的。


1
在更改和添加可为null的情况下,请使用AddColumn(“ dbo.table”,“ Created”,c => c.DateTime(nullable:true,defaultValueSql:“ getdate()”));
伊曼

这不是一个好主意,如果您选择数据库,则需要重构此代码。
lails

22

我已经在EF core 2.1上测试过

在这里,您不能使用约定或数据注释。您必须使用Fluent API

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

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

官方文件


12

这是可能且非常简单的:

对于 DateTime.MinValue

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

对于任何其他值,作为DefaultValueAttribute表示所需DateTime值的指定字符串的最后一个参数。

该值必须是常量表达式,并且是使用创建对象(DateTime)所必需的TypeConverter


我想相信使用对我有用,但不幸的是,它没有用。迁移结果;defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
Abdurrahman I.

只是尝试放置一个字符串日期(“ 1/1/20”)而不是空字符串,它成功地将其转换。
user890332,

5

如果使用的是实体框架,一个简单的解决方案是添加一个微粒类,并为实体定义一个构造函数,因为框架没有定义一个。例如,如果您有一个名为Example的实体,则可以将以下代码放在单独的文件中。

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

到目前为止,这是解决问题的最优雅(也是可能的)解决方案。但这是假设人们首先使用EF Code。

1
这就是我的做法,直到我意识到向实体中添加复杂属性会导致EF生成默认构造函数,从而使我无法编写自己的方法……
Dave Cousineau

5

我认为最简单的解决方案是设置

Created DATETIME2 NOT NULL DEFAULT GETDATE()

在列声明和VS2010 EntityModel设计器中,设置了相应的列属性StoreGeneratedPattern = Computed


如果这样做,并使该属性可为空,则会收到EDMX验证错误,因为不可为空的列已映射到可为空的属性。
尼克拉斯·彼得

4

创建一个新的属性类是一个很好的建议。就我而言,我想指定'default(DateTime)'或'DateTime.MinValue',以便Newtonsoft.Json序列化程序将忽略没有实际值的DateTime成员。

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

如果没有DefaultValue属性,则即使设置了DefaultValueHandling.Ignore选项,JSON序列化程序仍将输出“ 1/1/0001 12:00:00 AM”。


4

只需考虑在您的实体类的构造函数中设置其值

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

}

这对我有用。尽管Datetime在执行此操作之前必须将属性设置为可空。
Ahashan Alam Sojib

3

我需要一个UTC时间戳作为默认值,因此修改了Daniel的解决方案,如下所示:

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

有关DateRangeAttribute教程,请参阅此很棒的博客文章


3

使用System.ComponentModel.DataAnnotations.Schema;

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

1
请检查如何回答
Morse

2

有一种方法。添加以下类:

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

并将DefaultDateTimeValue用作属性的属性。输入到验证属性的值是“ Now”之类的东西,它将在运行时从使用Activator创建的DateTime实例中呈现。源代码受此线程启发:https : //code.msdn.microsoft.com/A-flexible-Default-Value-11c2db19。我对其进行了更改,以使我的类继承自DefaultValueAttribute而不是ValidationAttribute。


2

我遇到了同样的问题,但最适合我的问题如下:

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

1

刚发现这是在寻找不同的东西,但是在新的C#版本中,您可以为此使用更短的版本:

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

目前,您如何处理此问题取决于您使用的是Linq to SQL或EntityFramework的模型?

在L2S中,您可以添加

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

        ExecuteDynamicInsert(instance);
    }
}

EF稍微复杂一些,有关EF商务逻辑的更多信息,请参见http://msdn.microsoft.com/zh-cn/library/cc716714.aspx


0

我知道这篇文章有些旧,但是有个建议可能会有所帮助。

我使用一个枚举来确定要在属性构造函数中设置的内容。

财产申报:

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

属性构造函数:

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

枚举:

Public Enum EInitialValue
   DateTime_Now
   DateTime_Min
   DateTime_Max
End Enum

0

认为您可以使用StoreGeneratedPattern = Identity(在模型设计器属性窗口中设置)。

我不会猜到那是怎么做的,但是在试图弄清楚这一点时,我注意到我的某些日期列已经默认为默认值,CURRENT_TIMESTAMP()而有些则没有。检查模型,我发现除了名称之外,两列之间的唯一区别是获得默认值的列已StoreGeneratedPattern设置为Identity

我本来没想到会这样,但是阅读说明,这很有意义:

确定在插入和更新操作期间是否将自动生成数据库中的相应列。

另外,尽管这确实使数据库列的默认值为“ now”,但我猜它实际上并未将该属性设置DateTime.Now为POCO。这对我来说不是问题,因为我有一个自定义的.tt文件,该文件已经将所有日期列设置为DateTime.Now自动设置(实际上,自己修改.tt文件并不难,特别是如果您拥有ReSharper并突出显示语法的话)插件。(较新版本的VS可能已经语法突出显示.tt文件,不确定。)

对我来说,问题是:如何使数据库列具有默认值,以便省略该列的现有查询仍然有效?以上设置对此起作用。

我尚未测试过,但设置此值也可能会干扰设置您自己的显式值。(我最初只是偶然发现了这一点,因为EF6 Database First以这种方式为我编写了该模型。)


0

在C#版本6中,可以提供默认值

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

1
欢迎使用StackOverflow:如果您发布代码,XML或数据示例,请在文本编辑器中突出显示这些行,然后单击编辑器工具栏上的“代码示例”按钮({})或使用键盘上的Ctrl + K进行格式化和语法突出显示它!
WhatsThePoint


-1

使用EF 7:

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

迁移脚本:

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

结果:

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

EF 7不存在,DateTime列不能标识,并且注释也不能创建该迁移脚本。如果这就是您的意思,则不必手动更改迁移脚本。正如此处已充分回答的那样,有些注释确实添加了默认值。
Gert Arnold

-5

我也想要这个,并提出了这个解决方案(我只使用日期部分-默认时间作为PropertyGrid默认值没有意义):

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

这只会创建一个新属性,您可以将其添加到DateTime属性中。例如,如果默认为DateTime.Now.Date:

[DefaultDate(0)]

32
警告!!!!!!这非常糟糕,应该从SO中删除。我只是花了几个小时来调试由该建议引起的问题。乍一看,它看起来不错,而且似乎可以正常工作。但这是一个陷阱。由于某种原因,属性需要静态值。它们被初始化一次。此后,值更改。因此,虽然您创建的第一个实例看起来不错,而随后的实例看起来很好,但它们实际上都使用了第一个值。一旦您的应用程序运行了一段时间,您就会注意到该问题并想知道为什么。并且在调试中,当您运行一分钟时,它将运行良好。谨防!
克里斯·海因斯

3
克里斯是对的。我发现了同样的问题。 不要使用此答案
sohtimsso1970年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.