Установка значения по умолчанию для свойства DateTime в DateTime.Now внутри атрибута System.ComponentModel Default Value Attrbute


97

Кто-нибудь знает, как я могу указать значение по умолчанию для свойства DateTime с помощью атрибута System.ComponentModel DefaultValue?

например, я пробую это:

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

И он ожидает, что значение будет постоянным выражением.

Это в контексте использования с динамическими данными ASP.NET. Я не хочу формировать столбец DateCreated, а просто предоставляю DateTime.Now, если его нет. Я использую Entity Framework в качестве уровня данных

Привет,

Андрей

Ответы:


95

Вы не можете сделать это с атрибутом, потому что это просто метаинформация, сгенерированная во время компиляции. Просто добавьте код в конструктор для инициализации даты, если это необходимо, создайте триггер и обработайте отсутствующие значения в базе данных или реализуйте метод получения таким образом, чтобы он возвращал DateTime.Now, если резервное поле не инициализировано.

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

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;

2
Спасибо, перед редактированием пошел другим путем и перехватил сеттер. Приветствую за помощь :-)
REA_ANDREW

10
Часть getможно упростить до:return dateCreated ?? DateTime.Now;
Всеволод Краснов

Это решение может быть проблемой, если вы хотите, чтобы ваши классы были POCO. В моем случае мой класс должен быть таким же ясным, как класс POCO, передаваемый через WCF.
Джейкоб

1
@zHs Это отлично сработало для меня, используя Database First с Entity Framework.
Джошуа К.

1
Пожалуйста, посмотрите этот ответ, если вы пришли сюда за атрибутом.
Сторм Мюллер,

60

Добавьте ниже в свойство DateTime

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

3
Это на самом деле то, что я искал, и он должен подняться выше. Многие другие ответы добавляют текущую дату при вставке объекта, однако это не влияет на схему (т.е. делает getdate () значением по умолчанию). Проблема для меня заключалась в том, что когда я хотел добавить запись непосредственно в таблицу (используя SQL), мне приходилось указывать дату (или оставлять ее NULL). Это гораздо лучшее решение. Спасибо.
Сторм Мюллер

8
.Computed предназначен для действий добавления и обновления. Используйте .Identity только для добавления.
Ренан Коэльо

1
Лучший ответ на сегодняшний день: следует добавить ссылку на System.ComponentModel.DataAnnotations.Schema для аннотации DatabaseGenerated
Язан Халайле

Какой часовой пояс будет использоваться? Будет ли это UTC?
Almis

25

Нет причин, по которым я могу придумать, что это невозможно сделать с помощью атрибута. Это может быть в отставке Microsoft. Кто знает.

Лучшее решение, которое я нашел, - использовать параметр defaultValueSql при первой миграции кода.

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

Мне не нравится часто используемое решение по установке его в конструкторе класса сущности, потому что, если что-либо, кроме Entity Framework, вставляет запись в эту таблицу, поле даты не получит значения по умолчанию. И идея использования триггера для решения этого дела мне кажется неправильной.


1
в случае изменения и добавления допускающих значение NULL используйте AddColumn ("dbo.table", "Created", c => c.DateTime (nullable: true, defaultValueSql: "getdate ()"));
Иман

Это не лучшая идея, если вы выберете свою базу данных, вам потребуется рефакторинг этого кода.
lails

24

Я тестировал это на ядре EF 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()");
    }
}

Официальный документ


Это именно то, что я хотел, однако, поскольку мое свойство находилось в BaseClass, я не мог (он создал новую таблицу для BaseClass). Однако проверка ChangeTracker с помощью этой ссылки решила эту проблему (установка временных меток «Создано» и «Обновлено»).
Джепп,

12

Можно и довольно просто:

за DateTime.MinValue

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

для любого другого значения в качестве последнего аргумента DefaultValueAttribute укажите строку, представляющую желаемое значение DateTime.

Это значение должно быть постоянным выражением и требуется для создания объекта ( DateTime) с помощью TypeConverter.


1
Хотелось бы верить, что использование у меня работает, но, к сожалению, нет. Результат миграции; defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
Абдуррахман I.

Просто попытался поставить дату в строке («1/1/20») вместо пустой строки, и он успешно ее преобразует.
user890332

5

Простым решением, если вы используете Entity Framework, является добавление частичного класса и определение конструктора для объекта, поскольку структура его не определяет. Например, если у вас есть объект с именем Example, вы должны поместить следующий код в отдельный файл.

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

Это, безусловно, наиболее элегантное (и, вероятно, предполагаемое) решение проблемы. Но это при условии, что люди используют EF Code First.

1
Вот как я это делаю, пока не понял, что добавление сложного свойства к моей сущности заставляет EF генерировать конструктор по умолчанию, в результате чего я не могу написать свой собственный ...
Дэйв Кузино,

5

Думаю, самое простое решение - установить

Created DATETIME2 NOT NULL DEFAULT GETDATE()

в объявлении столбца и в конструкторе VS2010 EntityModel установите соответствующее свойство столбца StoreGeneratedPattern = Computed .


Если я сделаю это так и сделаю свойство обнуляемым, я получу ошибку проверки EDMX, потому что столбец, не допускающий значения NULL, сопоставляется со свойством, допускающим значение NULL.
Никлас Питер

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 сериализатор JSON будет выводить «1/1/0001 12:00:00 AM», даже если был установлен параметр DefaultValueHandling.Ignore.


4

Просто подумайте о том, чтобы установить его значение в конструкторе вашего класса сущности.

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

}

Это сработало для меня. Хотя Datetimeперед этим мне пришлось установить свойство, допускающее значение NULL.
Ахашан Алам Соджиб

3

Мне нужна была отметка времени UTC в качестве значения по умолчанию, поэтому я изменил решение Дэниела следующим образом:

    [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 см. Этот замечательный пост в блоге



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 в качестве атрибута ваших свойств. Значение для ввода в ваш атрибут проверки - это такие вещи, как «Сейчас», которые будут отображаться во время выполнения из экземпляра DateTime, созданного с помощью Activator. Исходный код вдохновлен этим потоком: https://code.msdn.microsoft.com/A-f flexible-Default-Value-11c2db19 . Я изменил его, чтобы мой класс унаследовал атрибут DefaultValueAttribute вместо ValidationAttribute.


2

Просто нашел это в поисках чего-то другого, но в новой версии C # вы можете использовать для этого еще более короткую версию:

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

Отличный ответ, именно то, что я хотел
Gjaa

Всегда ли это безопасно?
Йеппе,

не могу придумать никаких недостатков, официальная документация мало говорит об автоинициализации docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
Brandtware

1

Я столкнулся с той же проблемой, но тот, который подходит мне лучше всего, ниже:

public DateTime CreatedOn { 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 немного сложнее, см. Http://msdn.microsoft.com/en-us/library/cc716714.aspx для получения дополнительной информации о бизнес-логике EF.


0

Я знаю, что этот пост немного устарел, но у меня есть предложение, которое может некоторым помочь.

Я использовал Enum, чтобы определить, что установить в конструкторе атрибутов.

Декларация собственности:

[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

Enum:

Public Enum EInitialValue
   DateTime_Now
   DateTime_Min
   DateTime_Max
End Enum

0

Я думаю, вы можете сделать это, используяStoreGeneratedPattern = Identity (набор в окне конструктора свойств модели).

Я бы не догадался, как это можно сделать, но, пытаясь понять это, я заметил, что некоторые из моих столбцов дат уже имеют значения по умолчанию, CURRENT_TIMESTAMP()а некоторые нет. Проверяя модель, я вижу, что единственное различие между двумя столбцами, помимо имени, состоит в том, что тот, который получает значение по умолчанию, StoreGeneratedPatternустановлен на Identity.

Я бы не ожидал, что так будет, но, прочитав описание, это имеет смысл:

Определяет, будет ли автоматически создаваться соответствующий столбец в базе данных во время операций вставки и обновления.

Кроме того, хотя при этом столбец базы данных имеет значение по умолчанию «сейчас», я предполагаю, что на самом деле он не устанавливает свойство 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 08

1
То же, что и ответ Brandtware stackoverflow.com/a/47528230/52277
Майкл Фрейджейм,

-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 не может быть идентификатором, и аннотация не создает этот сценарий миграции. И нет необходимости вручную изменять сценарий миграции, если вы это имеете в виду. Есть аннотации, которые добавляют значение по умолчанию, как уже было подробно сказано здесь.
Герт Арнольд,

-6

Я также хотел этого и придумал это решение (я использую только часть даты - время по умолчанию не имеет смысла в качестве значения по умолчанию для PropertyGrid):

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

Это просто создает новый атрибут, который вы можете добавить к свойству DateTime. Например, если по умолчанию используется DateTime.Now.Date:

[DefaultDate(0)]

33
ПРЕДУПРЕЖДЕНИЕ!!!!!! это очень плохо и должно быть удалено из SO. Я просто часами отлаживал проблемы, вызванные этим предложением. На первый взгляд, выглядит неплохо и вроде работает. Но это ловушка. Атрибуты требуют статических значений по какой-то причине. Они инициализируются один раз. После этого значение изменится. Таким образом, хотя первый созданный вами экземпляр будет выглядеть нормально, а последующие экземпляры будут выглядеть нормально, все они фактически используют первое значение. Как только ваше приложение поработает какое-то время, вы заметите проблему и задаетесь вопросом, почему. И при отладке, когда вы запустите его на минуту, он будет работать нормально. ОСТОРОЖНО!
Крис Хайнс,

3
Крис прав. Я обнаружил ту же проблему. Не используйте этот ответ .
sohtimsso1970
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.