Mengatur nilai default dari Properti DateTime ke DateTime. Sekarang di dalam System.ComponentModel Nilai Default Attrbute

97

Apakah ada yang tahu bagaimana saya dapat menentukan nilai Default untuk properti DateTime menggunakan Atribut DefaultValue System.ComponentModel?

misalnya saya coba ini:

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

Dan itu mengharapkan nilainya menjadi ekspresi konstan.

Ini dalam konteks penggunaan dengan Data Dinamis ASP.NET. Saya tidak ingin menancapkan kolom DateCreated tetapi hanya menyediakan DateTime.Now jika tidak ada. Saya menggunakan Entity Framework sebagai Lapisan Data saya

Bersulang,

Andrew

REA_ANDREW
sumber

Jawaban:

95

Anda tidak dapat melakukan ini dengan atribut karena hanya informasi meta yang dihasilkan pada waktu kompilasi. Cukup tambahkan kode ke konstruktor untuk menginisialisasi tanggal jika diperlukan, buat pemicu dan tangani nilai yang hilang dalam database, atau terapkan getter dengan cara mengembalikan DateTime. Sekarang jika bidang dukungan tidak diinisialisasi.

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

   set { this.dateCreated = value; }
}

private DateTime? dateCreated = null;
Daniel Brückner
sumber
2
Terima kasih, sebelum Anda mengedit saya pergi ke arah lain dan menghalangi penyetel. Bersulang untuk bantuannya :-)
REA_ANDREW
10
Sebagian getdapat disederhanakan menjadi:return dateCreated ?? DateTime.Now;
Vsevolod Krasnov
Solusi ini mungkin menjadi masalah jika Anda ingin kelas Anda sebagai POCO. Dalam kasus saya, kelas saya harus sejelas kelas POCO untuk ditransfer melalui WCF.
Jacob
1
@zHs Ini bekerja dengan baik untuk saya menggunakan Database First dengan Entity Framework.
Joshua K
1
Silakan lihat jawaban ini jika Anda datang ke sini untuk suatu atribut.
Storm Muller
59

Tambahkan di bawah ke properti DateTime

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
pengguna2660763
sumber
3
Ini sebenarnya yang saya cari dan seharusnya bisa lebih tinggi. Banyak dari jawaban lain menambahkan tanggal saat ini ketika memasukkan entitas, namun itu tidak mempengaruhi skema (yaitu membuat getdate () nilai default). Masalah bagi saya adalah ketika saya ingin menambahkan catatan langsung ke tabel (menggunakan SQL) saya harus memberikan tanggal (atau membiarkannya NULL). Ini adalah solusi yang jauh lebih baik. Terima kasih.
Storm Muller
8
.Computed adalah untuk Tambahkan dan Perbarui tindakan. Gunakan .Identity untuk Add saja.
Renan Coelho
1
Jawaban terbaik sejauh ini: harus ditambahkan bahwa referensi ke System.ComponentModel.DataAnnotations.Schema harus ditambahkan untuk anotasi DatabaseGenerated
Yazan Khalaileh
Zona waktu apa yang akan digunakan? Apakah akan menjadi UTC?
Almis
25

Tidak ada alasan saya dapat menyimpulkan bahwa hal itu tidak mungkin dilakukan melalui atribut. Mungkin ada di backlog Microsoft. Siapa tahu.

Solusi terbaik yang saya temukan adalah menggunakan parameter defaultValueSql dalam migrasi kode pertama.

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

Saya tidak suka solusi referensi yang sering mengaturnya di konstruktor kelas entitas karena jika ada selain Entity Framework yang menempelkan catatan di tabel itu, bidang tanggal tidak akan mendapatkan nilai default. Dan gagasan menggunakan pemicu untuk menangani kasus itu sepertinya salah bagi saya.

d512
sumber
1
dalam hal mengubah dan menambahkan nullable gunakan AddColumn ("dbo.table", "Created", c => c.DateTime (nullable: true, defaultValueSql: "getdate ()"));
Iman
Bukan ide yang baik, jika Anda akan memilih db Anda, Anda perlu refactor kode ini.
lails
24

Saya telah menguji ini pada EF inti 2.1

Di sini Anda tidak dapat menggunakan Konvensi atau Anotasi Data. Anda harus menggunakan 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()");
    }
}

Dokumen resmi

Sampath
sumber
Ini persis seperti yang saya inginkan, namun karena properti saya berada di BaseClass, saya tidak bisa (ini membuat tabel baru untuk BaseClass). Namun, memeriksa ChangeTracker menggunakan tautan ini menyelesaikannya (menyetel cap waktu Dibuat dan Diperbarui).
Jeppe
12

Ini mungkin dan cukup sederhana:

untuk DateTime.MinValue

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

untuk nilai lain sebagai argumen terakhir dari DefaultValueAttribute menentukan string yang mewakili nilai DateTime yang diinginkan.

Nilai ini harus berupa ekspresi konstan dan diperlukan untuk membuat object ( DateTime) menggunakan TypeConverter.

DSW
sumber
1
Saya ingin percaya bahwa penggunaan berhasil untuk saya, tetapi sayangnya tidak. Hasil migrasi; defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
Abdurrahman I.
Baru saja mencoba untuk meletakkan tanggal string ("1/1/20") sebagai ganti string kosong dan berhasil mengubahnya.
user890332
5

Solusi sederhana jika Anda menggunakan Entity Framework adalah dengan menambahkan kelas partikal dan menentukan konstruktor untuk entitas karena kerangka tidak menentukannya. Misalnya jika Anda memiliki entitas bernama Contoh, Anda akan meletakkan kode berikut di file terpisah.

namespace EntityExample
{
    public partial class Example : EntityObject
    {
        public Example()
        {
            // Initialize certain default values here.
            this._DateCreated = DateTime.Now;
        }
    }
}
Terence Golla
sumber
Sejauh ini, ini adalah solusi yang paling elegan (dan mungkin juga dimaksudkan) untuk masalah tersebut. Tapi ini dengan asumsi orang menggunakan EF Code First.
1
Beginilah cara saya melakukannya, sampai saya menyadari bahwa menambahkan properti kompleks ke entitas saya menyebabkan EF menghasilkan konstruktor default, sehingga saya tidak dapat menulis sendiri ...
Dave Cousineau
5

Saya pikir solusi termudah adalah mengatur

Created DATETIME2 NOT NULL DEFAULT GETDATE()

dalam deklarasi kolom dan desainer VS2010 EntityModel mengatur properti kolom yang sesuai StoreGeneratedPattern = Computed .

Robertas
sumber
Jika saya melakukannya seperti ini dan menjadikan properti nullable, saya mendapatkan error validasi EDMX, karena kolom non-nullable dipetakan ke properti nullable.
Niklas Peter
4

Membuat kelas atribut baru adalah saran yang bagus. Dalam kasus saya, saya ingin menentukan 'default (DateTime)' atau 'DateTime.MinValue' sehingga serializer Newtonsoft.Json akan mengabaikan anggota DateTime tanpa nilai yang sebenarnya.

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

Tanpa atribut DefaultValue, serializer JSON akan mengeluarkan "1/1/0001 12:00:00 AM" meskipun opsi DefaultValueHandling.Ignore telah disetel.

Erhhung
sumber
4

Cukup pertimbangkan untuk menetapkan nilainya di konstruktor kelas entitas Anda

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

}
Muhammad Soliman
sumber
Ini berhasil untuk saya. Meskipun saya harus mengatur Datetimeproperti nullable sebelum melakukan ini.
Ahashan Alam Sojib
3

Saya membutuhkan Stempel Waktu UTC sebagai nilai default dan karenanya mengubah solusi Daniel seperti ini:

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

Untuk tutorial DateRangeAttribute, lihat posting blog yang luar biasa ini

Nuri
sumber
3

menggunakan System.ComponentModel.DataAnnotations.Schema;

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedOn { get; private set; }
Ali Kleit
sumber
1
Silakan periksa bagaimana menjawab
Morse
2

Ada jalan. Tambahkan kelas-kelas ini:

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

Dan gunakan DefaultDateTimeValue sebagai atribut ke properti Anda. Nilai yang akan dimasukkan ke atribut validasi Anda adalah hal-hal seperti "Sekarang", yang akan ditampilkan pada waktu proses dari instance DateTime yang dibuat dengan Activator. Kode sumber terinspirasi dari utas ini: https://code.msdn.microsoft.com/A-flexible-Default-Value-11c2db19 . Saya mengubahnya untuk membuat kelas saya mewarisi dengan DefaultValueAttribute daripada ValidationAttribute.

David
sumber
2

Baru saja menemukan ini mencari sesuatu yang berbeda, tetapi di versi C # yang baru, Anda dapat menggunakan versi yang lebih pendek untuk itu:

public DateTime DateCreated { get; set; } = DateTime.Now;
Brandtware
sumber
Jawaban yang luar biasa, seperti yang saya inginkan
Gjaa
Apakah ini selalu aman digunakan?
Jeppe
tidak dapat memikirkan kekurangan apa pun, dokumentasi resmi tidak banyak menjelaskan tentang inisialisasi otomatis docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
Brandtware
1

Saya menghadapi masalah yang sama, tetapi yang paling berhasil untuk saya adalah di bawah ini:

public DateTime CreatedOn { get; set; } = DateTime.Now;
pengguna3675084
sumber
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
sumber
0

Bagaimana Anda menangani hal ini saat ini bergantung pada model apa yang Anda gunakan Linq ke SQL atau EntityFramework?

Di L2S Anda bisa menambahkan

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

        ExecuteDynamicInsert(instance);
    }
}

EF sedikit lebih rumit, lihat http://msdn.microsoft.com/en-us/library/cc716714.aspx untuk info lebih lanjut tentang logika buisiness EF.

Wizzard
sumber
0

Saya tahu posting ini agak tua, tetapi memiliki saran yang mungkin membantu beberapa.

Saya menggunakan Enum untuk menentukan apa yang harus diatur di konstruktor atribut.

Deklarasi properti:

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

Konstruktor properti:

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
vincenormand.dll
sumber
0

Saya pikir Anda bisa melakukan ini dengan menggunakanStoreGeneratedPattern = Identity (diatur di jendela properti perancang model).

Saya tidak akan menyangka bahwa itu akan menjadi cara melakukannya, tetapi ketika mencoba untuk mengetahuinya saya perhatikan bahwa beberapa kolom tanggal saya sudah default CURRENT_TIMESTAMP()dan beberapa tidak. Memeriksa model, saya melihat bahwa satu-satunya perbedaan antara dua kolom selain nama adalah yang mendapatkan nilai default telah StoreGeneratedPatterndiatur ke Identity.

Saya tidak menyangka akan seperti itu, tetapi membaca deskripsinya, agak masuk akal:

Menentukan apakah kolom terkait dalam database akan dibuat secara otomatis selama operasi penyisipan dan pembaruan.

Selain itu, meskipun ini membuat kolom database memiliki nilai default "sekarang", saya rasa ini tidak benar-benar mengatur properti untuk berada DateTime.Nowdi POCO. Ini tidak menjadi masalah bagi saya karena saya memiliki file .tt yang disesuaikan yang sudah mengatur semua kolom tanggal saya DateTime.Nowsecara otomatis (sebenarnya tidak sulit untuk memodifikasi file .tt sendiri, terutama jika Anda memiliki ReSharper dan mendapatkan penyorotan sintaks plugin. (Versi VS yang lebih baru mungkin sudah menyorot file .tt sintaks, tidak yakin.))

Masalah bagi saya adalah: bagaimana cara membuat kolom database memiliki default sehingga kueri yang sudah ada yang menghilangkan kolom itu akan tetap berfungsi? Dan pengaturan di atas berfungsi untuk itu.

Saya belum mengujinya tetapi mungkin juga pengaturan ini akan mengganggu pengaturan nilai eksplisit Anda sendiri. (Saya hanya tersandung pada ini di tempat pertama karena EF6 Database First menulis model untuk saya seperti ini.)

Dave Cousineau
sumber
0

Dalam C # Versi 6 dimungkinkan untuk memberikan nilai default

public DateTime fieldname { get; set; } = DateTime.Now;
happycoding
sumber
1
Selamat datang di StackOverflow: jika Anda mengeposkan kode, XML, atau contoh data, harap sorot baris tersebut di editor teks dan klik tombol "contoh kode" ({}) pada bilah alat editor atau gunakan Ctrl + K pada keyboard Anda untuk memformat dengan baik dan sorot sintaks!
WhatsThePoint
1
Sama dengan jawaban Brandtware stackoverflow.com/a/47528230/52277
Michael Freidgeim
-1

Dengan EF 7:

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

skrip migrasi:

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

hasil:

ALTER TABLE [MySchema].[MyTable] ADD  CONSTRAINT [DF_MySchema.MyTable_Dateadded]  DEFAULT (getutcdate()) FOR [Dateadded]
Justin
sumber
EF 7 tidak ada, kolom DateTime tidak bisa menjadi identitas, dan anotasi tidak membuat skrip migrasi tersebut. Dan tidak perlu mengubah skrip migrasi secara manual, jika itu yang Anda maksud. Ada anotasi yang menambahkan default, seperti yang sudah banyak dijawab di sini.
Gert Arnold
-6

Saya juga menginginkan ini dan menghasilkan solusi ini (saya hanya menggunakan bagian tanggal - waktu default tidak masuk akal sebagai default PropertyGrid):

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

Ini hanya membuat atribut baru yang dapat Anda tambahkan ke properti DateTime Anda. Misalnya jika defaultnya adalah DateTime.Now.Date:

[DefaultDate(0)]
Michel Smits
sumber
33
PERINGATAN!!!!!! ini sangat buruk dan harus dihapus dari SO. Saya hanya menghabiskan waktu berjam-jam untuk men-debug masalah yang disebabkan oleh saran ini. Pada pandangan pertama, ini terlihat bagus dan berfungsi. Tapi ini jebakan. Atribut memerlukan nilai statis karena suatu alasan. Mereka diinisialisasi sekali. Setelah itu, nilainya berubah. Jadi, sementara contoh pertama yang Anda buat akan terlihat baik-baik saja, dan contoh berikutnya akan terlihat baik-baik saja, mereka semua sebenarnya menggunakan nilai pertama. Setelah aplikasi Anda berjalan beberapa saat, Anda akan melihat masalahnya dan bertanya-tanya mengapa. Dan dalam debug, ketika Anda menjalankannya sebentar, itu akan berjalan dengan baik. AWAS!
Chris Hynes
3
Chris benar. Saya menemukan masalah yang sama. Jangan gunakan jawaban ini .
sohtimsso1970