एंटिटी फ्रेमवर्क डेटाइम और यूटीसी


96

क्या एंटिटी फ्रेमवर्क होना संभव है (मैं वर्तमान में CTP5 के साथ कोड फर्स्ट अप्रोच का उपयोग कर रहा हूं) डेटाबेस में UTC के रूप में सभी दिनांक समय मानों को संग्रहीत करें?

या हो सकता है कि मैपिंग में इसे निर्दिष्ट करने का एक तरीका हो, उदाहरण के लिए अंतिम एक में यह कॉलम_ के लिए है:

modelBuilder.Entity<User>().Property(x => x.Id).HasColumnName("id");
modelBuilder.Entity<User>().Property(x => x.IsAdmin).HasColumnName("admin");
modelBuilder.Entity<User>().Property(x => x.IsEnabled).HasColumnName("enabled");
modelBuilder.Entity<User>().Property(x => x.PasswordHash).HasColumnName("password_hash");
modelBuilder.Entity<User>().Property(x => x.LastLogin).HasColumnName("last_login");

जवाबों:


144

यहाँ एक दृष्टिकोण है जिस पर आप विचार कर सकते हैं:

सबसे पहले, इस निम्नलिखित विशेषता को परिभाषित करें:

[AttributeUsage(AttributeTargets.Property)]
public class DateTimeKindAttribute : Attribute
{
    private readonly DateTimeKind _kind;

    public DateTimeKindAttribute(DateTimeKind kind)
    {
        _kind = kind;
    }

    public DateTimeKind Kind
    {
        get { return _kind; }
    }

    public static void Apply(object entity)
    {
        if (entity == null)
            return;

        var properties = entity.GetType().GetProperties()
            .Where(x => x.PropertyType == typeof(DateTime) || x.PropertyType == typeof(DateTime?));

        foreach (var property in properties)
        {
            var attr = property.GetCustomAttribute<DateTimeKindAttribute>();
            if (attr == null)
                continue;

            var dt = property.PropertyType == typeof(DateTime?)
                ? (DateTime?) property.GetValue(entity)
                : (DateTime) property.GetValue(entity);

            if (dt == null)
                continue;

            property.SetValue(entity, DateTime.SpecifyKind(dt.Value, attr.Kind));
        }
    }
}

अब हुक जो आपके EF के संदर्भ में है:

public class MyContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }

    public MyContext()
    {
        ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
            (sender, e) => DateTimeKindAttribute.Apply(e.Entity);
    }
}

अब किसी भी DateTimeया DateTime?गुणों पर, आप इस विशेषता को लागू कर सकते हैं:

public class Foo
{
    public int Id { get; set; }

    [DateTimeKind(DateTimeKind.Utc)]
    public DateTime Bar { get; set; }
}

इसके साथ, जब भी Entity Framework डेटाबेस से किसी निकाय को लोड करता है, तो यह DateTimeKindआपके द्वारा निर्दिष्ट किए गए सेट को सेट करेगा , जैसे UTC।

ध्यान दें कि बचत करते समय यह कुछ नहीं करता है। इससे पहले कि आप इसे सहेजने का प्रयास करें, आपको अभी भी यूटीसी में मूल्य को ठीक से परिवर्तित करना होगा। लेकिन यह आपको पुन: प्राप्त करते समय तरह सेट करने की अनुमति देता है, जो इसे यूटीसी के रूप में क्रमबद्ध करने की अनुमति देता है, या अन्य समय क्षेत्रों में परिवर्तित किया जाता है TimeZoneInfo


7
यदि आपको यह काम नहीं मिल रहा है, तो आप शायद इनमें से किसी एक को याद कर रहे हैं: सिस्टम का उपयोग करना; System.Collections.Generic का उपयोग कर; System.ComponentModel.DataAnnotations.Schema का उपयोग करना; System.Linq का उपयोग करना; System.Reflection का उपयोग करना;
साएसेप

7
@ साओसुप - आपको एसओ पर सबसे अधिक उदाहरण मिलेंगे, जब तक वे सीधे सवाल के लिए प्रासंगिक नहीं होंगे, तब तक संक्षिप्तता के लिए उपयोग छोड़ देंगे। लेकिन धन्यवाद।
मैट जॉनसन-पिंट

4
@MattJohnson बिना @ Saustrup के बयानों का उपयोग करते हुए, आपको कुछ अनछुई संकलन त्रुटियाँ मिल सकती हैं जैसे कि'System.Array' does not contain a definition for 'Where'
याकूब एगर

7
जैसा कि @SilverSideDown ने कहा, यह केवल .NET 4.5 के साथ काम करता है। मैंने इसे .NET 4.0 के साथ gist.github.com/munr/3544bd7fab6615290561 पर संगत करने के लिए कुछ एक्सटेंशन बनाए हैं । ध्यान देने वाली एक और बात यह है कि यह अनुमानों के साथ काम नहीं करेगा, केवल पूरी तरह से भरी हुई संस्थाएं।
मुन

5
यह अनुमानों के साथ जा रहा है पर कोई सुझाव?
जफिन

32

मैं वास्तव में मैट जॉनसन के दृष्टिकोण को पसंद करता हूं, लेकिन मेरे मॉडल में मेरे सभी डेटटाइम सदस्य यूटीसी हैं और मैं उन सभी को एक विशेषता के साथ सजाने के लिए नहीं चाहता हूं। इसलिए मैंने मैट के दृष्टिकोण को सामान्य बनाने की अनुमति दी कि जब तक किसी सदस्य को स्पष्ट रूप से विशेषता के साथ सजाया नहीं जाता है, तब तक इवेंट हैंडलर को डिफ़ॉल्ट रूप से लागू करने के लिए अनुमति देता है।

ApplicationDbContext क्लास के लिए कंस्ट्रक्टर में यह कोड शामिल है:

/// <summary> Constructor: Initializes a new ApplicationDbContext instance. </summary>
public ApplicationDbContext()
        : base(MyApp.ConnectionString, throwIfV1Schema: false)
{
    // Set the Kind property on DateTime variables retrieved from the database
    ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
      (sender, e) => DateTimeKindAttribute.Apply(e.Entity, DateTimeKind.Utc);
}

DateTimeKindAttribute इस तरह दिखता है:

/// <summary> Sets the DateTime.Kind value on DateTime and DateTime? members retrieved by Entity Framework. Sets Kind to DateTimeKind.Utc by default. </summary>
[AttributeUsage(AttributeTargets.Property)]
public class DateTimeKindAttribute : Attribute
{
    /// <summary> The DateTime.Kind value to set into the returned value. </summary>
    public readonly DateTimeKind Kind;

    /// <summary> Specifies the DateTime.Kind value to set on the returned DateTime value. </summary>
    /// <param name="kind"> The DateTime.Kind value to set on the returned DateTime value. </param>
    public DateTimeKindAttribute(DateTimeKind kind)
    {
        Kind = kind;
    }

    /// <summary> Event handler to connect to the ObjectContext.ObjectMaterialized event. </summary>
    /// <param name="entity"> The entity (POCO class) being materialized. </param>
    /// <param name="defaultKind"> [Optional] The Kind property to set on all DateTime objects by default. </param>
    public static void Apply(object entity, DateTimeKind? defaultKind = null)
    {
        if (entity == null) return;

        // Get the PropertyInfos for all of the DateTime and DateTime? properties on the entity
        var properties = entity.GetType().GetProperties()
            .Where(x => x.PropertyType == typeof(DateTime) || x.PropertyType == typeof(DateTime?));

        // For each DateTime or DateTime? property on the entity...
        foreach (var propInfo in properties) {
            // Initialization
            var kind = defaultKind;

            // Get the kind value from the [DateTimekind] attribute if it's present
            var kindAttr = propInfo.GetCustomAttribute<DateTimeKindAttribute>();
            if (kindAttr != null) kind = kindAttr.Kind;

            // Set the Kind property
            if (kind != null) {
                var dt = (propInfo.PropertyType == typeof(DateTime?))
                    ? (DateTime?)propInfo.GetValue(entity)
                    : (DateTime)propInfo.GetValue(entity);

                if (dt != null) propInfo.SetValue(entity, DateTime.SpecifyKind(dt.Value, kind.Value));
            }
        }
    }
}

1
यह स्वीकृत उत्तर के लिए बहुत उपयोगी विस्तार है!
लर्नर

शायद मुझे कुछ याद आ रहा है, लेकिन DateTimeKind.Utc के विरोध में DateTimeKind.Utc के लिए यह डिफ़ॉल्ट कैसे है?
रेनॉज

1
@Rononage इस बारे में क्षमा करें। डिफॉल्ट ApplicationDbContext कंस्ट्रक्टर में सेट किया गया है। मैंने उस उत्तर को शामिल करने के लिए अद्यतन किया।
Bob.at.Indigo.Health

1
@ Bob.at.AIPsychLab धन्यवाद दोस्त, अब बहुत स्पष्ट है। यह पता लगाने की कोशिश कर रहा था कि क्या कुछ वजन परावर्तन चल रहा है - लेकिन नहीं, मृत सरल!
रौनक

यह विफल रहता है यदि किसी मॉडल में DateTImeएक (सार्वजनिक) सेटर विधि के बिना एक विशेषता है। सुझाव संपादित करें। यह भी देखें stackoverflow.com/a/3762475/2279059
फ्लोरियन विंटर

13

यह उत्तर एंटिटी फ्रेमवर्क 6 के साथ काम करता है

स्वीकृत उत्तर अनुमानित या बेनामी वस्तु के लिए काम नहीं करता है। प्रदर्शन भी एक समस्या हो सकती है।

इसे प्राप्त करने के लिए, हमें DbCommandInterceptorEntityFramework द्वारा प्रदान की गई एक वस्तु का उपयोग करने की आवश्यकता है ।

इंटरसेप्टर बनाएं:

public class UtcInterceptor : DbCommandInterceptor
{
    public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        base.ReaderExecuted(command, interceptionContext);

        if (interceptionContext?.Result != null && !(interceptionContext.Result is UtcDbDataReader))
        {
            interceptionContext.Result = new UtcDbDataReader(interceptionContext.Result);
        }
    }
}

interceptionContext.Result DDDataReader है, जिसे हम अपनी जगह लेते हैं

public class UtcDbDataReader : DbDataReader
{
    private readonly DbDataReader source;

    public UtcDbDataReader(DbDataReader source)
    {
        this.source = source;
    }

    public override DateTime GetDateTime(int ordinal)
    {
        return DateTime.SpecifyKind(source.GetDateTime(ordinal), DateTimeKind.Utc);
    }        

    // you need to fill all overrides. Just call the same method on source in all cases

    public new void Dispose()
    {
        source.Dispose();
    }

    public new IDataReader GetData(int ordinal)
    {
        return source.GetData(ordinal);
    }
}

इंटरसेप्टर को अपने में रजिस्टर करें DbConfiguration

internal class MyDbConfiguration : DbConfiguration
{
    protected internal MyDbConfiguration ()
    {           
        AddInterceptor(new UtcInterceptor());
    }
}

अंत में, आपके लिए कॉन्फ़िगरेशन रजिस्टर करें DbContext

[DbConfigurationType(typeof(MyDbConfiguration ))]
internal class MyDbContext : DbContext
{
    // ...
}

बस। चीयर्स।

सादगी के लिए, यहाँ DbReader का संपूर्ण कार्यान्वयन है:

using System;
using System.Collections;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace MyNameSpace
{
    /// <inheritdoc />
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface")]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
    public class UtcDbDataReader : DbDataReader
    {
        private readonly DbDataReader source;

        public UtcDbDataReader(DbDataReader source)
        {
            this.source = source;
        }

        /// <inheritdoc />
        public override int VisibleFieldCount => source.VisibleFieldCount;

        /// <inheritdoc />
        public override int Depth => source.Depth;

        /// <inheritdoc />
        public override int FieldCount => source.FieldCount;

        /// <inheritdoc />
        public override bool HasRows => source.HasRows;

        /// <inheritdoc />
        public override bool IsClosed => source.IsClosed;

        /// <inheritdoc />
        public override int RecordsAffected => source.RecordsAffected;

        /// <inheritdoc />
        public override object this[string name] => source[name];

        /// <inheritdoc />
        public override object this[int ordinal] => source[ordinal];

        /// <inheritdoc />
        public override bool GetBoolean(int ordinal)
        {
            return source.GetBoolean(ordinal);
        }

        /// <inheritdoc />
        public override byte GetByte(int ordinal)
        {
            return source.GetByte(ordinal);
        }

        /// <inheritdoc />
        public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
        {
            return source.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
        }

        /// <inheritdoc />
        public override char GetChar(int ordinal)
        {
            return source.GetChar(ordinal);
        }

        /// <inheritdoc />
        public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
        {
            return source.GetChars(ordinal, dataOffset, buffer, bufferOffset, length);
        }

        /// <inheritdoc />
        public override string GetDataTypeName(int ordinal)
        {
            return source.GetDataTypeName(ordinal);
        }

        /// <summary>
        /// Returns datetime with Utc kind
        /// </summary>
        public override DateTime GetDateTime(int ordinal)
        {
            return DateTime.SpecifyKind(source.GetDateTime(ordinal), DateTimeKind.Utc);
        }

        /// <inheritdoc />
        public override decimal GetDecimal(int ordinal)
        {
            return source.GetDecimal(ordinal);
        }

        /// <inheritdoc />
        public override double GetDouble(int ordinal)
        {
            return source.GetDouble(ordinal);
        }

        /// <inheritdoc />
        public override IEnumerator GetEnumerator()
        {
            return source.GetEnumerator();
        }

        /// <inheritdoc />
        public override Type GetFieldType(int ordinal)
        {
            return source.GetFieldType(ordinal);
        }

        /// <inheritdoc />
        public override float GetFloat(int ordinal)
        {
            return source.GetFloat(ordinal);
        }

        /// <inheritdoc />
        public override Guid GetGuid(int ordinal)
        {
            return source.GetGuid(ordinal);
        }

        /// <inheritdoc />
        public override short GetInt16(int ordinal)
        {
            return source.GetInt16(ordinal);
        }

        /// <inheritdoc />
        public override int GetInt32(int ordinal)
        {
            return source.GetInt32(ordinal);
        }

        /// <inheritdoc />
        public override long GetInt64(int ordinal)
        {
            return source.GetInt64(ordinal);
        }

        /// <inheritdoc />
        public override string GetName(int ordinal)
        {
            return source.GetName(ordinal);
        }

        /// <inheritdoc />
        public override int GetOrdinal(string name)
        {
            return source.GetOrdinal(name);
        }

        /// <inheritdoc />
        public override string GetString(int ordinal)
        {
            return source.GetString(ordinal);
        }

        /// <inheritdoc />
        public override object GetValue(int ordinal)
        {
            return source.GetValue(ordinal);
        }

        /// <inheritdoc />
        public override int GetValues(object[] values)
        {
            return source.GetValues(values);
        }

        /// <inheritdoc />
        public override bool IsDBNull(int ordinal)
        {
            return source.IsDBNull(ordinal);
        }

        /// <inheritdoc />
        public override bool NextResult()
        {
            return source.NextResult();
        }

        /// <inheritdoc />
        public override bool Read()
        {
            return source.Read();
        }

        /// <inheritdoc />
        public override void Close()
        {
            source.Close();
        }

        /// <inheritdoc />
        public override T GetFieldValue<T>(int ordinal)
        {
            return source.GetFieldValue<T>(ordinal);
        }

        /// <inheritdoc />
        public override Task<T> GetFieldValueAsync<T>(int ordinal, CancellationToken cancellationToken)
        {
            return source.GetFieldValueAsync<T>(ordinal, cancellationToken);
        }

        /// <inheritdoc />
        public override Type GetProviderSpecificFieldType(int ordinal)
        {
            return source.GetProviderSpecificFieldType(ordinal);
        }

        /// <inheritdoc />
        public override object GetProviderSpecificValue(int ordinal)
        {
            return source.GetProviderSpecificValue(ordinal);
        }

        /// <inheritdoc />
        public override int GetProviderSpecificValues(object[] values)
        {
            return source.GetProviderSpecificValues(values);
        }

        /// <inheritdoc />
        public override DataTable GetSchemaTable()
        {
            return source.GetSchemaTable();
        }

        /// <inheritdoc />
        public override Stream GetStream(int ordinal)
        {
            return source.GetStream(ordinal);
        }

        /// <inheritdoc />
        public override TextReader GetTextReader(int ordinal)
        {
            return source.GetTextReader(ordinal);
        }

        /// <inheritdoc />
        public override Task<bool> IsDBNullAsync(int ordinal, CancellationToken cancellationToken)
        {
            return source.IsDBNullAsync(ordinal, cancellationToken);
        }

        /// <inheritdoc />
        public override Task<bool> ReadAsync(CancellationToken cancellationToken)
        {
            return source.ReadAsync(cancellationToken);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly")]
        public new void Dispose()
        {
            source.Dispose();
        }

        public new IDataReader GetData(int ordinal)
        {
            return source.GetData(ordinal);
        }
    }
}

अब तक यह सबसे अच्छा जवाब लगता है। मैंने विशेषता भिन्नता को पहले आज़माया था क्योंकि यह कम दूर तक पहुँचने वाली लग रही थी, लेकिन मेरी यूनिट परीक्षण मज़ाक के साथ विफल हो जाती है क्योंकि ऑनरोडेलक्रिएटिंग इवेंट में होने वाले टेबल मैपिंग के बारे में कंस्ट्रक्टर इवेंट टाई-इन को नहीं जानता है। यह एक मेरा वोट हो जाता है!
सीनेटर

1
तुम क्यों पीछा कर रहे हैं Disposeऔर GetData?
user247702

2
इस कोड को शायद @IvanStoev: stackoverflow.com/a/40349051/90287
Rami A.

दुर्भाग्य से यह विफल हो जाता है यदि आप स्थानिक डेटा मैप कर रहे हैं
क्रिस

@ user247702 yea शैडोइंग डिस्पोज़ गलती है, ओवरराइड
डिस्पोज़

9

मेरा मानना ​​है कि मुझे ऐसा समाधान मिल गया है जिसमें किसी भी कस्टम यूटीसी चेकिंग या डेटटाइम हेरफेर की आवश्यकता नहीं है।

मूल रूप से आपको DateTimeOffset (NOT DateTime) डेटाटाइप का उपयोग करने के लिए अपनी EF संस्थाओं को बदलने की आवश्यकता है। यह डेटाबेस में दिनांक मान (SQL Server 2015 मेरे मामले में) के साथ समय क्षेत्र संग्रहीत करेगा।

जब EF Core DB से डेटा का अनुरोध करता है तो उसे टाइमज़ोन जानकारी भी प्राप्त होगी। जब आप इस डेटा को एक वेब एप्लिकेशन (मेरे मामले में Angular2) पास करते हैं, तो तारीख स्वचालित रूप से ब्राउज़र के स्थानीय समयक्षेत्र में परिवर्तित हो जाती है, जो मुझे उम्मीद है।

और जब इसे मेरे सर्वर पर वापस भेज दिया जाता है तो इसे UTC में फिर से स्वचालित रूप से बदल दिया जाता है, जैसा कि अपेक्षित था।


7
DateTimeOffset आम धारणा के विपरीत, समय क्षेत्र को संग्रहीत नहीं करता है। यह यूटीसी से एक ऑफसेट स्टोर करता है जो मूल्य का प्रतिनिधित्व करता है। ऑफसेट को वास्तविक समय क्षेत्र निर्धारित करने के लिए रिवर्स में मैप नहीं किया जा सकता है, जिससे ऑफसेट बनाया गया था, जिससे डेटाटाइप लगभग बेकार हो गया था।
सूर्यास्त २२

2
नहीं, लेकिन इसका उपयोग डेट डेट को
कार्ल

1
केवल UTC को स्थान की आवश्यकता नहीं है, क्योंकि यह हर जगह समान है। यदि आप UTC के अलावा किसी और चीज़ का उपयोग करते हैं, तो आपको स्थान की आवश्यकता भी है, अन्यथा समय की जानकारी बेकार है, साथ ही डेटाइमऑफ़सेट का उपयोग करने पर भी।
होरित्सू

@ Suncat2000 समय में एक बिंदु को स्टोर करने का सबसे समझदार तरीका है। अन्य सभी दिनांक / समय प्रकार आपको या तो समय क्षेत्र नहीं देते हैं।
जॉन

1
DATETIMEOFFSET वही करेगा जो मूल पोस्टर चाहता था: किसी भी (स्पष्ट) रूपांतरण के बिना UTC के रूप में दिनांक-समय संग्रहीत करें। @ कार्ल डेटेट, DATETIME2, और DATETIMEOFFSET सभी दुकान दिनांक-समय मान को सही ढंग से। इसके अलावा, UTC से ऑफसेट संग्रहीत करने के अलावा, DATETIMEOFFSET का लगभग कोई फायदा नहीं है। आप अपने डेटाबेस में जो उपयोग करते हैं वह आपकी कॉल है। मैं बस इस बिंदु पर घर चलाना चाहता था कि यह समय क्षेत्र को स्टोर न करे जैसा कि कई लोग गलती से सोचते हैं।
Suncat2000

5

डेटा टाइमकेइंड को एंटिटी फ्रेमवर्क में निर्दिष्ट करने का कोई तरीका नहीं है। आप db को संग्रहीत करने से पहले utc करने के लिए दिनांक समय मान को परिवर्तित करने का निर्णय ले सकते हैं और हमेशा db से पुनर्प्राप्त डेटा को UTC के रूप में मान सकते हैं। लेकिन क्वेरी के दौरान बनाए गए डेटटाइम ऑब्जेक्ट्स हमेशा "अनिर्दिष्ट" होंगे। आप DateTime के बजाय DateTimeOffset ऑब्जेक्ट का उपयोग करके भी स्पष्ट कर सकते हैं।


5

मैं अभी इस पर शोध कर रहा हूं, और इनमें से अधिकांश उत्तर बिल्कुल महान नहीं हैं। मैं जो देख सकता हूं, उसमें EF6 को बताने का कोई तरीका नहीं है कि डेटाबेस से निकलने की तारीखें UTC प्रारूप में हैं। अगर ऐसा है, तो यह सुनिश्चित करने का सबसे सरल तरीका है कि आपके मॉडल की डेटटाइम प्रॉपर्टीज UTC में हैं और वेटर में वेरीफाई और कन्वर्ट करना होगा।

यहाँ pseudocode की तरह कुछ c # है जो एल्गोरिथ्म का वर्णन करता है

public DateTime MyUtcDateTime 
{    
    get 
    {        
        return _myUtcDateTime;        
    }
    set
    {   
        if(value.Kind == DateTimeKind.Utc)      
            _myUtcDateTime = value;            
        else if (value.Kind == DateTimeKind.Local)         
            _myUtcDateTime = value.ToUniversalTime();
        else 
            _myUtcDateTime = DateTime.SpecifyKind(value, DateTimeKind.Utc);        
    }    
}

पहली दो शाखाएँ स्पष्ट हैं। आखिरी में गुप्त सॉस है।

जब EF6 डेटाबेस से लोड किए गए डेटा से एक मॉडल बनाता है, तो DateTimes होते हैं DateTimeKind.Unspecified। यदि आप जानते हैं कि आपकी तिथियाँ db में सभी UTC हैं, तो अंतिम शाखा आपके लिए बहुत अच्छा काम करेगी।

DateTime.Nowहमेशा होता है DateTimeKind.Local, इसलिए उपरोक्त एल्गोरिथ्म कोड में उत्पन्न तारीखों के लिए ठीक काम करता है। सर्वाधिक समय।

हालांकि, आपको सतर्क रहना होगा क्योंकि अन्य तरीके DateTimeKind.Unspecifiedआपके कोड में घुस सकते हैं। उदाहरण के लिए, आप JSON डेटा से अपने मॉडल और इस तरह के अपने deserializer फ्लेवर डिफॉल्ट से अपने मॉडल को डिसेरिएलाइज़ कर सकते हैं। यह आप पर निर्भर है DateTimeKind.Unspecifiedकि किसी से लेकिन EF से उस सेटर में चिह्नित स्थानीयकृत तिथियों की रक्षा करें ।


6
जैसा कि मैंने इस मुद्दे के साथ कुश्ती के कई वर्षों के बाद पता लगाया, यदि आप डेटाइम फ़ील्ड को अन्य संरचनाओं में असाइन कर रहे हैं या चयन कर रहे हैं, उदाहरण के लिए डेटा ट्रांसफर ऑब्जेक्ट, ईएफ दोनों गटर और सेटर विधियों की उपेक्षा करता है। इन मामलों में, आपको DateTimeKind.Utcअपने परिणाम उत्पन्न होने के बाद भी किंड को बदलना होगा । उदाहरण: from o in myContext.Records select new DTO() { BrokenTimestamp = o.BbTimestamp };सभी प्रकार को सेट करता है DateTimeKind.Unspecified
सूर्यास्त २२

1
मैं थोड़ी देर के लिए Entity फ्रेमवर्क के साथ DateTimeOffset का उपयोग कर रहा हूं और यदि आप अपने EF संस्थाओं को डेटा टाइम डेटाइम ऑफ़सेट के साथ निर्दिष्ट करते हैं, तो आपके सभी EF क्वेरीज़ UTC से ऑफ़सेट के साथ दिनांक वापस कर देंगे, ठीक उसी तरह जैसे यह DB में सहेजा गया है। इसलिए यदि आपने अपने डेटा प्रकार को DateTimeOffset में बदल दिया है तो DateTime के बजाय आपको उपरोक्त वर्कअराउंड की आवश्यकता नहीं होगी।
मॉटूनो

पता चला तो अच्छा है! धन्यवाद @Moutono

@ Suncat2000 टिप्पणी के अनुसार, यह बस काम नहीं करता है और इसे हटा दिया जाना चाहिए
बेन मॉरिस

5

के लिए एफई कोर , वहाँ GitHub पर इस विषय पर एक महान चर्चा है: https://github.com/dotnet/efcore/issues/4711

एक समाधान ( क्रिस्टोफर हॉस के लिए क्रेडिट ) जिसके परिणामस्वरूप सभी तिथियों के उपचार में परिणाम आएगा जब उन्हें यूटीसी के रूप में डेटाबेस से पुनर्प्राप्त / पुनः प्राप्त करना होगा ताकि निम्नलिखित को OnModelCreatingआपकी DbContextकक्षा की विधि में जोड़ा जा सके :

var dateTimeConverter = new ValueConverter<DateTime, DateTime>(
    v => v.ToUniversalTime(),
    v => DateTime.SpecifyKind(v, DateTimeKind.Utc));

var nullableDateTimeConverter = new ValueConverter<DateTime?, DateTime?>(
    v => v.HasValue ? v.Value.ToUniversalTime() : v,
    v => v.HasValue ? DateTime.SpecifyKind(v.Value, DateTimeKind.Utc) : v);

foreach (var entityType in builder.Model.GetEntityTypes())
{
    if (entityType.IsQueryType)
    {
        continue;
    }

    foreach (var property in entityType.GetProperties())
    {
        if (property.ClrType == typeof(DateTime))
        {
            property.SetValueConverter(dateTimeConverter);
        }
        else if (property.ClrType == typeof(DateTime?))
        {
            property.SetValueConverter(nullableDateTimeConverter);
        }
    }
}

इसके अलावा, इस लिंक की जाँच करें यदि आप कुछ संस्थाओं की कुछ संपत्तियों को UTC के रूप में माना जाना चाहते हैं।


निश्चित रूप से मेरे लिए सबसे अच्छा समाधान! धन्यवाद
बेन मॉरिस

क्या यह DateTimeOffset के साथ काम करता है?
मार्क रेडमैन

1
@MarkRedman मुझे नहीं लगता कि यह समझ में आता है, क्योंकि यदि आपके पास DateTimeOffset के लिए वैध उपयोग का मामला है, तो आप समय क्षेत्र के बारे में भी जानकारी रखना चाहते हैं। DateTime और DateTimeOffset के बीच चयन करने के लिए docs.microsoft.com/en-us/dotnet/standard/datetime/… या stackoverflow.com/a/14268167/3979621 देखें ।
होनज़ा कालफस

IsQueryTypeलगता है कि इसके द्वारा प्रतिस्थापित किया गया है IsKeyLess: github.com/dotnet/efcore/commit/…
मार्क टाईलेमेन

4

यदि आप मूल्यों को निर्धारित करते समय UTC तिथियों को ठीक से पास करने के लिए सावधान हैं और आप सभी के बारे में परवाह करते हैं, तो सुनिश्चित करें कि DateTimeKind डेटाबेस से पुनर्प्राप्त किए जाने पर ठीक से सेट है, मेरा जवाब यहां देखें: https://stackoverflow.com/ एक / 9386364/279590


3

एक और साल, एक और उपाय! यह EF Core के लिए है।

मेरे पास बहुत सारे DATETIME2(7)कॉलम हैं जो मैप करते हैंDateTime हमेशा , और हमेशा यूटीसी स्टोर करते हैं। मैं एक ऑफसेट स्टोर नहीं करना चाहता क्योंकि अगर मेरा कोड सही है तो ऑफसेट हमेशा शून्य रहेगा।

इस बीच मेरे पास अन्य कॉलम्स हैं जो अज्ञात ऑफ़सेट (उपयोगकर्ताओं द्वारा प्रदान किए गए) के मूल दिनांक-समय मानों को संग्रहीत करते हैं, इसलिए उन्हें "जैसा है" केवल संग्रहीत / प्रदर्शित किया जाता है और किसी भी चीज़ के साथ तुलना नहीं की जाती है।

इसलिए मुझे एक समाधान की आवश्यकता है जिसे मैं विशिष्ट कॉलम पर लागू कर सकता हूं।

एक विस्तार विधि को परिभाषित करें UsesUtc:

private static DateTime FromCodeToData(DateTime fromCode, string name)
    => fromCode.Kind == DateTimeKind.Utc ? fromCode : throw new InvalidOperationException($"Column {name} only accepts UTC date-time values");

private static DateTime FromDataToCode(DateTime fromData) 
    => fromData.Kind == DateTimeKind.Unspecified ? DateTime.SpecifyKind(fromData, DateTimeKind.Utc) : fromData.ToUniversalTime();

public static PropertyBuilder<DateTime?> UsesUtc(this PropertyBuilder<DateTime?> property)
{
    var name = property.Metadata.Name;
    return property.HasConversion<DateTime?>(
        fromCode => fromCode != null ? FromCodeToData(fromCode.Value, name) : default,
        fromData => fromData != null ? FromDataToCode(fromData.Value) : default
    );
}

public static PropertyBuilder<DateTime> UsesUtc(this PropertyBuilder<DateTime> property)
{
    var name = property.Metadata.Name;
    return property.HasConversion(fromCode => FromCodeToData(fromCode, name), fromData => FromDataToCode(fromData));
}

यह तब मॉडल सेटअप में गुणों पर उपयोग किया जा सकता है:

modelBuilder.Entity<CustomerProcessingJob>().Property(x => x.Started).UsesUtc();

विशेषताओं पर इसका मामूली लाभ है कि आप इसे केवल सही प्रकार के गुणों पर लागू कर सकते हैं।

ध्यान दें कि यह मान लेता है कि DB से मान UTC में हैं, लेकिन सिर्फ गलत है Kind। इसलिए यह उन मूल्यों को दर्शाता है जो आप DB में स्टोर करने की कोशिश करते हैं, यदि वे यूटीसी नहीं हैं तो एक वर्णनात्मक अपवाद फेंकते हैं।


1
यह एक महान समाधान है जो विशेष रूप से अब अधिक होना चाहिए कि ज्यादातर नए विकास कोर या .NET का उपयोग करेंगे। UTC प्रवर्तन नीति के लिए बोनस काल्पनिक अंक - यदि अधिक लोग अपनी तारीखों को यूटीसी वास्तविक उपयोगकर्ता के प्रदर्शन के लिए सभी तरह से रखते हैं, हम शायद ही किसी भी तारीख / समय कीड़े होगा।
oflahero

1

उन लोगों के लिए जिन्हें मेरे जैसे। नेट फ्रेमवर्क 4 के साथ @MattJohnson समाधान प्राप्त करने की आवश्यकता है, प्रतिबिंब सिंटैक्स / विधि सीमा के साथ, इसे नीचे सूचीबद्ध के रूप में थोड़ा सा संशोधन की आवश्यकता है:

     foreach (var property in properties)
        {     

            DateTimeKindAttribute attr  = (DateTimeKindAttribute) Attribute.GetCustomAttribute(property, typeof(DateTimeKindAttribute));

            if (attr == null)
                continue;

            var dt = property.PropertyType == typeof(DateTime?)
                ? (DateTime?)property.GetValue(entity,null)
                : (DateTime)property.GetValue(entity, null);

            if (dt == null)
                continue;

            //If the value is not null set the appropriate DateTimeKind;
            property.SetValue(entity, DateTime.SpecifyKind(dt.Value, attr.Kind) ,null);
        }  

1

मैट जॉनसन-पिंट का समाधान काम करता है, लेकिन यदि आपके सभी DateTimes UTC माना जाता है, तो एक विशेषता बनाना बहुत अधिक सर्किट होगा। यहां बताया गया है कि मैंने इसे कैसे सरल बनाया:

public class MyContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }

    public MyContext()
    {
        ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
            (sender, e) => SetDateTimesToUtc(e.Entity);
    }

    private static void SetDateTimesToUtc(object entity)
    {
        if (entity == null)
        {
            return;
        }

        var properties = entity.GetType().GetProperties();
        foreach (var property in properties)
        {
            if (property.PropertyType == typeof(DateTime))
            {
                property.SetValue(entity, DateTime.SpecifyKind((DateTime)property.GetValue(entity), DateTimeKind.Utc));
            }
            else if (property.PropertyType == typeof(DateTime?))
            {
                var value = (DateTime?)property.GetValue(entity);
                if (value.HasValue)
                {
                    property.SetValue(entity, DateTime.SpecifyKind(value.Value, DateTimeKind.Utc));
                }
            }
        }
    }
}

0

एक अन्य दृष्टिकोण डेटाटाइम गुणों के साथ एक इंटरफ़ेस बनाना होगा, उन्हें आंशिक इकाई वर्गों पर लागू करना होगा। और फिर SaveChanges ईवेंट का उपयोग यह जांचने के लिए करें कि क्या ऑब्जेक्ट इंटरफ़ेस प्रकार का है, उन डेटाटाइम मानों को जो आप चाहते हैं सेट करें। वास्तव में, यदि ये डेटोन / मॉडिफ़ाइड तरह के डेट हैं, तो आप उन्हें पॉप्युलेट करने के लिए उस इवेंट का उपयोग कर सकते हैं।


0

मेरे मामले में, मेरे पास UTC डेटासेट के साथ केवल एक तालिका थी। यहाँ मैंने क्या किया है:

public partial class MyEntity
{
    protected override void OnPropertyChanged(string property)
    {
        base.OnPropertyChanged(property);            

        // ensure that values coming from database are set as UTC
        // watch out for property name changes!
        switch (property)
        {
            case "TransferDeadlineUTC":
                if (TransferDeadlineUTC.Kind == DateTimeKind.Unspecified)
                    TransferDeadlineUTC = DateTime.SpecifyKind(TransferDeadlineUTC, DateTimeKind.Utc);
                break;
            case "ProcessingDeadlineUTC":
                if (ProcessingDeadlineUTC.Kind == DateTimeKind.Unspecified)
                    ProcessingDeadlineUTC = DateTime.SpecifyKind(ProcessingDeadlineUTC, DateTimeKind.Utc);
            default:
                break;
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.