Встановлення значення за замовчуванням властивості DateTime до DateTime. Тепер всередині System.ComponentModel Значення за замовчуванням Attrbute


94

Хтось знає, як я можу вказати значення за замовчуванням для властивості DateTime за допомогою атрибута System.ComponentModel DefaultValue?

наприклад, я пробую це:

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

І воно очікує, що значення буде постійним виразом.

Це в контексті використання з ASP.NET динамічних даних. Я не хочу встановлювати на стіл стовпець DateCreated, а просто ввести DateTime. Тепер, якщо його немає. Я використовую Entity Framework як свій рівень даних

Ура,

Андрій

Відповіді:


93

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

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

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;

2
Дякую, перед редагуванням я пішов іншим шляхом і перехопив сетера.
Вітаємо

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

Це рішення може бути проблемою, якщо ви хочете, щоб ваші класи були POCO. У моєму випадку мій клас повинен бути таким же чітким, як клас POCO, який буде передаватися через WCF.
Яків

1
@zHs Це чудово працювало для мене, використовуючи Database First with Entity Framework.
Джошуа К

1
Будь ласка, перегляньте цю відповідь, якщо ви прийшли сюди за атрибутом.
Storm Muller

55

Додайте нижче до властивості DateTime

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

3
Це власне те, що я шукав, і воно повинно підніматися вище. Багато інших відповідей додають поточну дату при вставці сутності, однак це не впливає на схему (тобто робить getdate () типовим значенням). Проблема для мене полягала в тому, що коли я хотів додати запис безпосередньо в таблицю (за допомогою SQL), мені довелося вказати дату (або залишити її НУЛЬНОЮ). Це набагато краще рішення. Дякую.
Storm Muller

8
Обчислюється для дій додавання та оновлення. Використовуйте .Identity лише для додавання.
Ренан Коельо

1
Найкраща відповідь на сьогодні: слід додати, що посилання на System.ComponentModel.DataAnnotations.Schema слід додати для анотації DatabaseGenerated
Yazan Khalaileh

Який часовий пояс буде використовуватися? Це буде UTC?
Алміс,

25

Немає причин, за якими я можу придумати, що це не може бути можливо через атрибут. Це може бути у відставанні Microsoft. Хто знає.

Найкраще рішення, яке я знайшов, - використовувати параметр defaultValueSql у першій міграції коду.

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

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


1
у разі зміни та додавання обнулюваного використовуйте AddColumn ("dbo.table", "Created", c => c.DateTime (nullable: true, defaultValueSql: "getdate ()"));
Іман

Це не гарна ідея, якщо ви виберете свій db, вам потрібен рефакторинг цього коду.
відстає

22

Я перевірив це на EF core 2.1

Тут ви не можете використовувати ні конвенції, ні анотації даних. Ви повинні використовувати 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()");
    }
}

Офіційний док


12

Це можливо і досить просто:

для DateTime.MinValue

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

для будь-якого іншого значення як останнього аргументу DefaultValueAttributeзаданого рядка, який представляє бажане значення DateTime.

Це значення має бути постійним виразом і потрібне для створення об'єкта ( DateTime) за допомогою TypeConverter.


Я хотів би вірити, що використання працює для мене, але, на жаль, ні. Результат міграції; defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
Абдуррахман І.

Просто спробував поставити дату рядка ("1/1/20") замість порожнього рядка, і вона успішно перетворює її.
user890332

5

Простим рішенням, якщо ви використовуєте Entity Framework, є додавання часткового класу та визначення конструктора для сутності, оскільки фреймворк його не визначає. Наприклад, якщо у вас є сутність з іменем Приклад, ви помістите наступний код у окремий файл.

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

Це, безумовно, найелегантніше (і, мабуть, також передбачуване) рішення проблеми. Але це припускає, що люди використовують EF Code First.

1
Ось як я це роблю, поки не зрозумів, що додавання складної властивості до моєї сутності змушує EF генерувати конструктор за замовчуванням, не залишаючи мені можливості писати свій власний ...
Dave Cousineau

5

Я думаю, що найпростішим рішенням є встановлення

Created DATETIME2 NOT NULL DEFAULT GETDATE()

у декларації стовпця та у дизайнері VS2010 EntityModel встановлено відповідну властивість стовпця StoreGeneratedPattern = Обчислюється .


Якщо я роблю це так і роблю властивість нульовим, я отримую помилку перевірки EDMX, тому що ненульований стовпець відображається у властивість, що допускає нуль.
Ніклас Пітер

4

Створення нового класу атрибутів - хороша порада. У моєму випадку я хотів вказати 'за замовчуванням (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 00:00:00 AM", навіть незважаючи на те, що було встановлено параметр DefaultValueHandling.Ignore.


4

Просто розгляньте можливість встановити його значення в конструкторі вашого класу сутності

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

}

Це спрацювало для мене. Хоча Datetimeперед цим мені довелося встановити властивість як нульове.
Ахашан Алам Соджіб

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, створеного за допомогою активатора. Вихідний код натхненний цим потоком: https://code.msdn.microsoft.com/A-flex-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 трохи складніший, див. 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

У версії 6 C # можна вказати значення за замовчуванням

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

1
Ласкаво просимо до StackOverflow: якщо ви розміщуєте поштовий індекс, XML або зразки даних, виділіть ці рядки в текстовому редакторі та натисніть кнопку "Зразки коду" ({}) на панелі інструментів редактора або використовуючи Ctrl + K на клавіатурі для гарного форматування та синтаксис виділіть це!
WhatsThePoint

1
Так само , як відповідь Brandtware в stackoverflow.com/a/47528230/52277
Майкл Freidgeim

-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 не може бути ідентичністю, а анотація не створює цей сценарій міграції. І не потрібно змінювати сценарій міграції вручну, якщо ви маєте на увазі саме це. Є анотації, які додають значення за замовчуванням, як уже було тут відповідено.
Герт Арнольд

-5

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

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

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

[DefaultDate(0)]

32
УВАГА!!!!!! це дуже погано, і його слід видалити з SO. Я просто годинами налагоджував проблеми, викликані цією пропозицією. На перший погляд, це виглядає добре і, здається, працює. Але це пастка. Атрибути вимагають статичних значень з причини. Вони ініціалізуються один раз. Після цього значення змінюється. Тож, хоча перший екземпляр, який ви створюєте, буде виглядати нормально, а наступні екземпляри будуть виглядати нормально, усі вони насправді використовують перше значення. Коли ваш додаток буде запущений деякий час, ви помітите проблему і здивуєтесь чому. І під час налагодження, коли ви запускаєте його протягом хвилини, він буде працювати нормально. ОСТОРОЖНО!
Chris Hynes

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