Ustawianie domyślnej wartości właściwości DateTime na DateTime.Now wewnątrz System.ComponentModel Wartość domyślna Attrbute


97

Czy ktoś wie, jak mogę określić wartość domyślną dla właściwości DateTime przy użyciu atrybutu System.ComponentModel DefaultValue?

na przykład próbuję tego:

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

I oczekuje, że wartość będzie stałym wyrażeniem.

Dzieje się tak w kontekście używania z danymi dynamicznymi ASP.NET. Nie chcę tworzyć szkieletu kolumny DateCreated, ale po prostu dostarczam DateTime.Now, jeśli nie jest obecny. Używam Entity Framework jako mojej warstwy danych

Twoje zdrowie,

Andrzej

Odpowiedzi:


95

Nie możesz tego zrobić za pomocą atrybutu, ponieważ są to tylko metainformacje generowane w czasie kompilacji. Wystarczy dodać kod do konstruktora, aby w razie potrzeby zainicjować datę, utworzyć wyzwalacz i obsłużyć brakujące wartości w bazie danych lub zaimplementować metodę pobierającą w taki sposób, aby zwracała DateTime.Now, jeśli pole zapasowe nie jest zainicjowane.

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

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;

2
Dziękuję, przed montażem poszedłem w drugą stronę i przechwyciłem rozgrywającego. Pozdrawiam pomoc :-)
REA_ANDREW

10
getCzęść można uprościć do:return dateCreated ?? DateTime.Now;
Vsevolod Krasnov

To rozwiązanie może stanowić problem, jeśli chcesz, aby Twoje zajęcia były typu POCO. W moim przypadku moja klasa powinna być tak przejrzysta, jak klasa POCO, która ma być przesłana przez WCF.
Jacob

1
@zHs To zadziałało świetnie dla mnie, używając Database First z Entity Framework.
Joshua K

1
Proszę zobaczyć tę odpowiedź, jeśli przyszedłeś tutaj po atrybut.
Storm Muller,

59

Dodaj poniżej do właściwości DateTime

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

3
Właśnie tego szukałem i powinno wznieść się wyżej. Wiele innych odpowiedzi dodaje bieżącą datę podczas wstawiania jednostki, jednak nie wpływa to na schemat (tj. Ustaw getdate () wartością domyślną). Problem polegał na tym, że gdy chciałem dodać rekord bezpośrednio do tabeli (używając SQL), musiałem podać datę (lub pozostawić ją NULL). To znacznie lepsze rozwiązanie. Dzięki.
Storm Muller,

8
.Computed służy do dodawania i aktualizacji. Użyj .Identity tylko do dodawania.
Renan Coelho

1
Najlepsza odpowiedź zdecydowanie: należy dodać, że odniesienie do System.ComponentModel.DataAnnotations.Schema powinny zostać dodane do adnotacji DatabaseGenerated
Yazan Khalaileh

Jaka strefa czasowa będzie używana? Czy to będzie UTC?
Almis

25

Nie ma powodu, dla którego mógłbym wymyślić, że nie powinno to być możliwe za pomocą atrybutu. Może być w zaległościach Microsoftu. Kto wie.

Najlepszym rozwiązaniem, jakie znalazłem, jest użycie parametru defaultValueSql w pierwszej migracji kodu.

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

Nie podoba mi się często referencyjne rozwiązanie polegające na ustawianiu go w konstruktorze klasy jednostki, ponieważ jeśli cokolwiek innego niż Entity Framework przechowuje rekord w tej tabeli, pole daty nie otrzyma wartości domyślnej. Pomysł użycia wyzwalacza do obsługi tej sprawy wydaje mi się po prostu zły.


1
w przypadku zmiany i dodania wartości null użyj AddColumn ("dbo.table", "Created", c => c.DateTime (nullable: true, defaultValueSql: "getdate ()"));
Iman,

To nie jest dobry pomysł, jeśli wybierzesz swoją bazę danych, potrzebujesz refaktoryzacji tego kodu.
kończy się

24

Testowałem to na EF Core 2.1

Tutaj nie możesz używać ani Konwencji, ani adnotacji danych. Musisz użyć interfejsu 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()");
    }
}

Oficjalny doc


Dokładnie tego chciałem, jednak ponieważ moja właściwość była w BaseClass, nie mogłem (utworzyła nową tabelę dla BaseClass). Jednak sprawdzenie ChangeTrackera za pomocą tego linku rozwiązało problem (ustawienie zarówno utworzonych, jak i zaktualizowanych znaczników czasu).
Jeppe

12

Jest to możliwe i całkiem proste:

dla DateTime.MinValue

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

dla dowolnej innej wartości jako ostatni argument DefaultValueAttribute ciągu określającego, który reprezentuje żądaną wartość DateTime.

Ta wartość musi być wyrażeniem stałym i jest wymagana do utworzenia obiektu ( DateTime) przy użyciu TypeConverter.


1
Chciałbym wierzyć, że użycie działa dla mnie, ale niestety tak się nie stało. Wynik migracji; defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
Abdurrahman I.

Po prostu próbowałem umieścić datę w postaci łańcucha („1/1/20”) zamiast pustego ciągu i pomyślnie go konwertuje.
user890332

5

Prostym rozwiązaniem, jeśli używasz Entity Framework, jest dodanie klasy częściowej i zdefiniowanie konstruktora dla jednostki, ponieważ struktura jej nie definiuje. Na przykład, jeśli masz jednostkę o nazwie Example, umieściłbyś następujący kod w oddzielnym pliku.

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

Jest to zdecydowanie najbardziej eleganckie (i prawdopodobnie również zamierzone) rozwiązanie problemu. Ale to zakłada, że ​​ludzie używają EF Code First.

1
Tak to robię, dopóki nie zdałem sobie sprawy, że dodanie złożonej właściwości do mojej encji powoduje, że EF generuje domyślny konstruktor, pozostawiając mnie bez możliwości napisania własnego ...
Dave Cousineau

5

Myślę, że najłatwiejszym rozwiązaniem jest ustawienie

Created DATETIME2 NOT NULL DEFAULT GETDATE()

w deklaracji kolumny iw projektancie EntityModel VS2010 ustaw odpowiednią właściwość kolumny StoreGeneratedPattern = Computed .


Jeśli zrobię to w ten sposób i sprawię, że właściwość dopuszcza wartość null, pojawia się błąd walidacji EDMX, ponieważ kolumna nie dopuszczająca wartości null jest zamapowana na właściwość dopuszczającą wartość null.
Niklas Peter

4

Utworzenie nowej klasy atrybutów to dobra sugestia. W moim przypadku chciałem określić „default (DateTime)” lub „DateTime.MinValue”, aby serializator Newtonsoft.Json ignorował członków DateTime bez wartości rzeczywistych.

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

Bez atrybutu DefaultValue serializator JSON wyprowadziłby „1/1/0001 12:00:00 AM”, mimo że ustawiono opcję DefaultValueHandling.Ignore.


4

Po prostu rozważ ustawienie jego wartości w konstruktorze klasy encji

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

}

To zadziałało dla mnie. Chociaż musiałem ustawić Datetimewłaściwość nullable przed wykonaniem tej czynności.
Ahashan Alam Sojib

3

Potrzebowałem znacznika czasu UTC jako wartości domyślnej, więc zmodyfikowałem rozwiązanie Daniela w następujący sposób:

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

Aby zapoznać się z samouczkiem DateRangeAttribute, zobacz ten niesamowity wpis na blogu


3

using System.ComponentModel.DataAnnotations.Schema;

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

1
Sprawdź, jak odpowiedzieć
Morse

2

Jest sposób. Dodaj te zajęcia:

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

I użyj DefaultDateTimeValue jako atrybutu do swoich właściwości. Wartość wprowadzana do atrybutu walidacji to rzeczy takie jak „Now”, które będą renderowane w czasie wykonywania z instancji DateTime utworzonej za pomocą Aktywatora. Kod źródłowy jest inspirowany tym wątkiem: https://code.msdn.microsoft.com/A-f flexible-Default-Value-11c2db19 . Zmieniłem to, aby moja klasa dziedziczyła z DefaultValueAttribute zamiast ValidationAttribute.


2

Właśnie znalazłem to szukając czegoś innego, ale w nowej wersji C # możesz użyć do tego jeszcze krótszej wersji:

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

Świetna odpowiedź, właśnie tego chciałem
Gjaa

Czy jest to zawsze bezpieczne w użyciu?
Jeppe

2

Napotkałem ten sam problem, ale ten, który działa najlepiej, jest poniżej:

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

Jak sobie z tym teraz radzisz, zależy od tego, jakiego modelu używasz Linq do SQL lub EntityFramework?

W L2S możesz dodać

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

        ExecuteDynamicInsert(instance);
    }
}

EF jest nieco bardziej skomplikowany, zobacz http://msdn.microsoft.com/en-us/library/cc716714.aspx, aby uzyskać więcej informacji na temat logiki biznesowej EF.


0

Wiem, że ten post jest trochę stary, ale mam sugestię, która może niektórym pomóc.

Użyłem Enum, aby określić, co ustawić w konstruktorze atrybutów.

Deklaracja własności:

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

Konstruktor nieruchomości:

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

Myślę, że możesz to zrobić za pomocąStoreGeneratedPattern = Identity (ustawionego w oknie właściwości projektanta modelu).

Nie przypuszczałbym, że tak to zrobić, ale próbując to rozgryźć, zauważyłem, że niektóre z moich kolumn z datami już się ustawiały, CURRENT_TIMESTAMP()a inne nie. Po sprawdzeniu modelu widzę, że jedyną różnicą między dwiema kolumnami oprócz nazwy jest to, że ta, która otrzymuje wartość domyślną, ma StoreGeneratedPatternustawioną wartośćIdentity .

Nie spodziewałbym się, że tak będzie, ale czytając opis, ma to sens:

Określa, czy odpowiednia kolumna w bazie danych zostanie automatycznie wygenerowana podczas operacji wstawiania i aktualizacji.

Ponadto, chociaż powoduje to, że kolumna bazy danych ma domyślną wartość „teraz”, wydaje mi się, że w rzeczywistości nie ustawia ona tej właściwości DateTime.Nowna POCO. Nie stanowiło to dla mnie problemu, ponieważ mam dostosowany plik .tt, który już ustawia wszystkie moje kolumny z datami na DateTime.Nowautomatyczne (w rzeczywistości nie jest trudno samodzielnie zmodyfikować plik .tt, zwłaszcza jeśli masz ReSharper i otrzymujesz podświetlanie składni plugin. (nowsze wersje VS mogą już wyróżniać pliki .tt, nie jestem pewien.))

Problem dla mnie brzmiał: jak sprawić, aby kolumna bazy danych miała wartość domyślną, aby istniejące zapytania, które pomijają tę kolumnę, nadal działały? I powyższe ustawienie zadziałało.

Nie testowałem tego jeszcze, ale możliwe jest również, że ustawienie tego będzie kolidowało z ustawieniem własnej jawnej wartości. (Natknąłem się na to tylko dlatego, że EF6 Database First napisał dla mnie model w ten sposób).


0

W C # w wersji 6 można podać wartość domyślną

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

1
Witamy w StackOverflow: jeśli wysyłasz kod, XML lub próbki danych, zaznacz te wiersze w edytorze tekstu i kliknij przycisk „próbki kodu” ({}) na pasku narzędzi edytora lub użyj Ctrl + K na klawiaturze, aby ładnie sformatować i podświetlić składnię!
WhatsThePoint

1
To samo, co odpowiedź firmy Brandtware stackoverflow.com/a/47528230/52277
Michael Freidgeim

-1

Z EF 7:

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

skrypt migracji:

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

wynik:

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

EF 7 nie istnieje, kolumna DateTime nie może być tożsamością, a adnotacja nie tworzy tego skryptu migracji. I nie jest konieczne ręczne zmienianie skryptu migracji, jeśli o to ci chodzi. Istnieją adnotacje, które dodają wartość domyślną, na co już tutaj udzielono obszernej odpowiedzi.
Gert Arnold

-6

Chciałem też tego i wymyśliłem takie rozwiązanie (używam tylko części daty - domyślny czas nie ma sensu jako domyślny PropertyGrid):

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

Spowoduje to po prostu utworzenie nowego atrybutu, który można dodać do właściwości DateTime. Np. Jeśli domyślnie jest to DateTime.Now.Date:

[DefaultDate(0)]

33
OSTRZEŻENIE!!!!!! jest to bardzo złe i powinno zostać usunięte z SO. Po prostu spędziłem godziny na debugowaniu problemów spowodowanych tą sugestią. Na pierwszy rzut oka wygląda dobrze i wydaje się działać. Ale to jest pułapka. Atrybuty nie bez powodu wymagają wartości statycznych. Są inicjalizowane raz. Następnie wartość się zmienia. Tak więc, chociaż pierwsza utworzona instancja będzie wyglądać dobrze, a kolejne instancje będą wyglądać dobrze, w rzeczywistości wszystkie używają pierwszej wartości. Gdy Twoja aplikacja będzie działać przez jakiś czas, zauważysz problem i zastanawiasz się, dlaczego. A w debugowaniu, gdy uruchomisz go na minutę, będzie działał dobrze. STRZEC SIĘ!
Chris Hynes,

3
Chris ma rację. Znalazłem ten sam problem. Nie używaj tej odpowiedzi .
sohtimsso1970
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.