Festlegen des Standardwerts einer DateTime-Eigenschaft auf DateTime.Now im System.ComponentModel-Standardwert Attrbute

91

Weiß jemand, wie ich den Standardwert für eine DateTime-Eigenschaft mithilfe des System.ComponentModel DefaultValue-Attributs angeben kann?

Zum Beispiel versuche ich Folgendes:

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

Und es wird erwartet, dass der Wert ein konstanter Ausdruck ist.

Dies steht im Zusammenhang mit der Verwendung mit dynamischen ASP.NET-Daten. Ich möchte die DateCreated-Spalte nicht als Gerüst verwenden, sondern nur die DateTime.Now angeben, wenn sie nicht vorhanden ist. Ich verwende das Entity Framework als meine Datenschicht

Prost,

Andrew

REA_ANDREW
quelle

Antworten:

91

Sie können dies nicht mit einem Attribut tun, da es sich lediglich um Metainformationen handelt, die zur Kompilierungszeit generiert werden. Fügen Sie dem Konstruktor einfach Code hinzu, um das Datum bei Bedarf zu initialisieren, einen Trigger zu erstellen und fehlende Werte in der Datenbank zu behandeln, oder implementieren Sie den Getter so, dass er DateTime.Now zurückgibt, wenn das Hintergrundfeld nicht initialisiert ist.

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

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;
Daniel Brückner
quelle
2
Vielen Dank, bevor Sie bearbeiten, bin ich in die andere Richtung gegangen und habe den Setter abgefangen. Prost auf die Hilfe :-)
REA_ANDREW
10
Ein getTeil kann vereinfacht werden, um:return dateCreated ?? DateTime.Now;
Wsewolod Krasnow
Diese Lösung kann ein Problem sein, wenn Sie Ihre Klassen als POCO haben möchten. In meinem Fall sollte meine Klasse so klar sein wie die POCO-Klasse, die über WCF übertragen werden soll.
Jacob
1
@zHs Dies hat bei der Verwendung von Database First mit Entity Framework hervorragend funktioniert.
Joshua K
1
Bitte lesen Sie diese Antwort, wenn Sie wegen eines Attributs hierher gekommen sind.
Sturm Müller
53

Fügen Sie unten die DateTime-Eigenschaft hinzu

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
user2660763
quelle
3
Dies ist eigentlich das, wonach ich gesucht habe und es sollte höher steigen. Viele der anderen Antworten fügen beim Einfügen einer Entität das aktuelle Datum hinzu, haben jedoch keine Auswirkungen auf das Schema (dh machen Sie getdate () zum Standardwert). Das Problem für mich war, als ich einen Datensatz direkt in die Tabelle einfügen wollte (unter Verwendung von SQL), musste ich das Datum angeben (oder es NULL lassen). Dies ist eine viel bessere Lösung. Vielen Dank.
Sturm Müller
8
Berechnet für Aktionen zum Hinzufügen und Aktualisieren. Verwenden Sie .Identity nur zum Hinzufügen.
Renan Coelho
1
Die mit Abstand beste Antwort: Es sollte hinzugefügt werden, dass ein Verweis auf System.ComponentModel.DataAnnotations.Schema für die Annotation DatabaseGenerated hinzugefügt werden sollte
Yazan Khalaileh
Welche Zeitzone wird verwendet? Wird es UTC sein?
Almis
25

Es gibt keinen Grund, warum ich mir vorstellen kann, dass es nicht möglich sein sollte, ein Attribut zu verwenden. Möglicherweise befindet es sich im Rückstand von Microsoft. Wer weiß.

Die beste Lösung, die ich gefunden habe, besteht darin, den Parameter defaultValueSql in der ersten Code-Migration zu verwenden.

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

Ich mag die häufig verwendete Referenzlösung zum Festlegen im Entitätsklassenkonstruktor nicht, da das Datumsfeld keinen Standardwert erhält, wenn etwas anderes als Entity Framework einen Datensatz in diese Tabelle einfügt. Und die Idee, einen Auslöser zu verwenden, um diesen Fall zu behandeln, scheint mir einfach falsch.

d512
quelle
1
Wenn Sie nullable ändern und hinzufügen, verwenden Sie AddColumn ("dbo.table", "Created", c => c.DateTime (nullable: true, defaultValueSql: "getdate ()").
Iman
Es ist keine gute Idee, wenn Sie Ihre Datenbank auswählen, müssen Sie diesen Code umgestalten.
Lails
21

Ich habe dies auf EF Core 2.1 getestet

Hier können Sie weder Konventionen noch Datenanmerkungen verwenden. Sie müssen die Fluent-API verwenden .

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

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

Offizielles Dokument

Sampath
quelle
11

Es ist möglich und ganz einfach:

zum DateTime.MinValue

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

für jeden anderen Wert als letztes Argument der DefaultValueAttributeangegebenen Zeichenfolge, die den gewünschten DateTime-Wert darstellt.

Dieser Wert muss ein konstanter Ausdruck sein und ist erforderlich, um object ( DateTime) mit zu erstellen TypeConverter.

DSW
quelle
Ich würde gerne glauben, dass die Nutzung für mich funktioniert, aber leider nicht. Migrationsergebnis; defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
Abdurrahman I.
5

Eine einfache Lösung, wenn Sie das Entity Framework verwenden, besteht darin, eine partielle Klasse hinzuzufügen und einen Konstruktor für die Entität zu definieren, da das Framework keine definiert. Wenn Sie beispielsweise eine Entität mit dem Namen "Beispiel" haben, fügen Sie den folgenden Code in eine separate Datei ein.

namespace EntityExample
{
    public partial class Example : EntityObject
    {
        public Example()
        {
            // Initialize certain default values here.
            this._DateCreated = DateTime.Now;
        }
    }
}
Terence Golla
quelle
Dies ist bei weitem die eleganteste (und wahrscheinlich auch die beabsichtigte) Lösung des Problems. Dies setzt jedoch voraus, dass Benutzer EF Code First verwenden.
1
So mache ich das, bis mir klar wurde, dass das Hinzufügen einer komplexen Eigenschaft zu meiner Entität dazu führt, dass EF einen Standardkonstruktor generiert, sodass ich keine Möglichkeit habe, meinen eigenen zu schreiben ...
Dave Cousineau
5

Ich denke, die einfachste Lösung ist das Einstellen

Created DATETIME2 NOT NULL DEFAULT GETDATE()

Legen Sie in der Spaltendeklaration und im VS2010 EntityModel-Designer die entsprechende Spalteneigenschaft StoreGeneratedPattern = Computed fest .

Robertas
quelle
Wenn ich es so mache und die Eigenschaft nullbar mache, erhalte ich einen EDMX-Validierungsfehler, weil eine nicht nullbare Spalte einer nullbaren Eigenschaft zugeordnet ist.
Niklas Peter
4

Das Erstellen einer neuen Attributklasse ist ein guter Vorschlag. In meinem Fall wollte ich 'default (DateTime)' oder 'DateTime.MinValue' angeben, damit der Newtonsoft.Json-Serializer DateTime-Mitglieder ohne echte Werte ignoriert.

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

Ohne das DefaultValue-Attribut würde der JSON-Serializer "1/1/0001 12:00:00 AM" ausgeben, obwohl die Option DefaultValueHandling.Ignore festgelegt wurde.

Erhhung
quelle
4

Stellen Sie den Wert einfach im Konstruktor Ihrer Entitätsklasse ein

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

}
Muhammad Soliman
quelle
Das hat bei mir funktioniert. Obwohl ich vorher die DatetimeEigenschaft auf null setzen musste.
Ahashan Alam Sojib
3

Ich brauchte einen UTC-Zeitstempel als Standardwert und modifizierte Daniels Lösung wie folgt:

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

Das DateRangeAttribute-Tutorial finden Sie in diesem fantastischen Blog-Beitrag

Nuri
quelle
3

using System.ComponentModel.DataAnnotations.Schema;

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedOn { get; private set; }
Ali Kleit
quelle
1
Bitte überprüfen Sie, wie zu antworten
Morse
2

Da ist ein Weg. Fügen Sie diese Klassen hinzu:

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

Verwenden Sie DefaultDateTimeValue als Attribut für Ihre Eigenschaften. Der Wert, der in Ihr Validierungsattribut eingegeben werden muss, ist "Jetzt", der zur Laufzeit von einer mit einem Aktivator erstellten DateTime-Instanz gerendert wird. Der Quellcode ist von diesem Thread inspiriert: https://code.msdn.microsoft.com/A-flexible-Default-Value-11c2db19 . Ich habe es geändert, damit meine Klasse mit DefaultValueAttribute anstelle eines ValidationAttribute erbt.

David
quelle
2

Ich hatte das gleiche Problem, aber das, das für mich am besten funktioniert, ist unten aufgeführt:

public DateTime CreatedOn { get; set; } = DateTime.Now;
user3675084
quelle
1

Ich habe gerade festgestellt, dass dies nach etwas anderem sucht, aber in der neuen C # -Version können Sie dafür eine noch kürzere Version verwenden:

public DateTime DateCreated { get; set; } = DateTime.Now;
Brandtware
quelle
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);
Bobac
quelle
0

Wie Sie derzeit damit umgehen, hängt davon ab, welches Modell Sie mit Linq to SQL oder EntityFramework verwenden.

In L2S können Sie hinzufügen

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

        ExecuteDynamicInsert(instance);
    }
}

EF ist etwas komplizierter. Weitere Informationen zur EF-Geschäftslogik finden Sie unter http://msdn.microsoft.com/en-us/library/cc716714.aspx .

Wizzard
quelle
0

Ich weiß, dass dieser Beitrag ein wenig alt ist, aber ich habe einen Vorschlag, der einigen helfen kann.

Ich habe eine Aufzählung verwendet, um zu bestimmen, was im Attributkonstruktor festgelegt werden soll.

Eigentumserklärung:

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

Eigenschaftskonstruktor:

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

Aufzählung:

Public Enum EInitialValue
   DateTime_Now
   DateTime_Min
   DateTime_Max
End Enum
vincenormand
quelle
0

Ich denke, Sie können dies mit StoreGeneratedPattern = Identity(im Eigenschaftenfenster des Modelldesigners festgelegt) tun .

Ich hätte nicht gedacht, dass dies so sein würde, aber als ich versuchte, es herauszufinden, bemerkte ich, dass einige meiner Datumsspalten bereits standardmäßig verwendet wurden CURRENT_TIMESTAMP()und andere nicht. Wenn ich das Modell überprüfe, sehe ich, dass der einzige Unterschied zwischen den beiden Spalten neben dem Namen darin besteht, dass diejenige, die den Standardwert erhält, auf StoreGeneratedPatterngesetzt wurde Identity.

Ich hätte nicht erwartet, dass das so ist, aber das Lesen der Beschreibung macht irgendwie Sinn:

Legt fest, ob die entsprechende Spalte in der Datenbank beim Einfügen und Aktualisieren automatisch generiert wird.

Auch wenn die Datenbankspalte dadurch den Standardwert "jetzt" hat, wird die Eigenschaft DateTime.Nowvermutlich nicht so festgelegt, dass sie sich im POCO befindet. Dies war für mich kein Problem, da ich eine angepasste .tt-Datei habe, in der bereits alle meine Datumsspalten DateTime.Nowautomatisch festgelegt sind (es ist eigentlich nicht schwer, die .tt-Datei selbst zu ändern, insbesondere wenn Sie über ReSharper verfügen und eine Syntaxhervorhebung erhalten Plugin. (Neuere Versionen von VS können bereits .tt-Dateien syntaktisch hervorheben, nicht sicher.))

Das Problem für mich war: Wie kann ich erreichen, dass die Datenbankspalte einen Standardwert hat, sodass vorhandene Abfragen, bei denen diese Spalte weggelassen wird, weiterhin funktionieren? Und die obige Einstellung hat dafür funktioniert.

Ich habe es noch nicht getestet, aber es ist auch möglich, dass das Einstellen dies die Einstellung Ihres eigenen expliziten Werts beeinträchtigt. (Ich bin erst darauf gestoßen, weil EF6 Database First das Modell auf diese Weise für mich geschrieben hat.)

Dave Cousineau
quelle
0

In C # Version 6 ist es möglich, einen Standardwert anzugeben

public DateTime fieldname { get; set; } = DateTime.Now;
happycoding
quelle
1
Willkommen bei StackOverflow: Wenn Sie Code-, XML- oder Datenbeispiele veröffentlichen, markieren Sie diese Zeilen im Texteditor und klicken Sie auf die Schaltfläche "Codebeispiele" ({}) in der Editor-Symbolleiste oder verwenden Sie Strg + K auf Ihrer Tastatur, um das Format zu optimieren und Syntax heben es hervor!
WhatsThePoint
1
Das gleiche wie Brandtwares Antwort stackoverflow.com/a/47528230/52277
Michael Freidgeim
-1

Mit EF 7:

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

Migrationsskript:

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

Ergebnis:

ALTER TABLE [MySchema].[MyTable] ADD  CONSTRAINT [DF_MySchema.MyTable_Dateadded]  DEFAULT (getutcdate()) FOR [Dateadded]
Justin
quelle
EF 7 existiert nicht, eine DateTime-Spalte kann keine Identität sein und die Anmerkung erstellt dieses Migrationsskript nicht. Und es ist nicht notwendig, das Migrationsskript manuell zu ändern, wenn Sie das meinen. Es gibt Anmerkungen, die den Standard hinzufügen, wie hier bereits ausführlich beantwortet wurde.
Gert Arnold
-5

Ich wollte das auch und habe mir diese Lösung ausgedacht (ich verwende nur den Datumsteil - eine Standardzeit macht als PropertyGrid-Standard keinen Sinn):

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

Dadurch wird lediglich ein neues Attribut erstellt, das Sie Ihrer DateTime-Eigenschaft hinzufügen können. Beispiel: Wenn der Standardwert DateTime.Now.Date ist:

[DefaultDate(0)]
Michel Smits
quelle
31
WARNUNG!!!!!! Dies ist sehr schlecht und sollte aus SO entfernt werden. Ich habe gerade Stunden damit verbracht, Probleme zu debuggen, die durch diesen Vorschlag verursacht wurden. Auf den ersten Blick sieht es gut aus und scheint zu funktionieren. Aber das ist eine Falle. Attribute erfordern aus einem bestimmten Grund statische Werte. Sie werden einmal initialisiert. Danach ändert sich der Wert. Während die erste von Ihnen erstellte Instanz gut aussieht und nachfolgende Instanzen gut aussehen, verwenden alle tatsächlich den ersten Wert. Sobald Ihre App eine Weile ausgeführt wurde, werden Sie das Problem bemerken und sich fragen, warum. Und beim Debuggen läuft es einwandfrei, wenn Sie es eine Minute lang ausführen. IN ACHT NEHMEN!
Chris Hynes
3
Chris hat recht. Ich habe das gleiche Problem gefunden. Verwenden Sie diese Antwort nicht .
Sohtimsso1970