সত্তা ফ্রেমওয়ার্ক ডেটটাইম এবং ইউটিসি


98

ডেটাবেসে ইউটিসি হিসাবে সমস্ত ডেটটাইম মানগুলি বর্তমানে সন্টি ফ্রেমওয়ার্ক (আমি বর্তমানে সিটিপি 5 এর সাথে কোড ফার্স্ট অ্যাপ্রোচ ব্যবহার করছি) থাকা কি সম্ভব?

অথবা ম্যাপিংয়ে এটি নির্দিষ্ট করার কোনও উপায় রয়েছে, উদাহরণস্বরূপ সর্বশেষ_লগিন কলামের জন্য এটিতে:

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

উত্তর:


147

আপনি বিবেচনা করতে পারেন এখানে একটি পদ্ধতির:

প্রথমে নিম্নলিখিত বৈশিষ্ট্যগুলি সংজ্ঞায়িত করুন:

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

এটি জায়গায়, যখনই সত্তা ফ্রেমওয়ার্ক ডাটাবেস থেকে কোনও সত্তা লোড করে, এটি DateTimeKindআপনাকে নির্দিষ্ট করে যেমন ইউটিসি সেট করে ।

মনে রাখবেন সংরক্ষণ করার সময় এটি কিছুই করে না। সংরক্ষণের চেষ্টা করার আগে আপনাকে এখনও ইউটিসিতে মানটি সঠিকভাবে রূপান্তর করতে হবে। তবে পুনরুদ্ধার করার সময় এটি আপনাকে ধরণের সেট করার অনুমতি দেয় না যা এটি ইউটিসি হিসাবে সিরিয়ালাইজ করার অনুমতি দেয় বা এর সাথে অন্য সময় অঞ্চলগুলিতে রূপান্তরিত করতে দেয় TimeZoneInfo


7
আপনি যদি এই কাজটি পেতে না পারেন তবে আপনি সম্ভবত এই ব্যবহারগুলির একটি অনুপস্থিত: সিস্টেম ব্যবহার করে; সিস্টেম.কলেশন.জেনারিক ব্যবহার করে; System.Comp घटकModel.DataAnnotations.Schema ব্যবহার করে; System.Linq ব্যবহার করে; সিস্টেম.রিফ্লেশন ব্যবহার করে;
সস্ট্রুপ

8
@ সৌস্ট্রুপ - আপনি প্রশ্নের সবচেয়ে প্রাসঙ্গিক না হলে আপনি বেশিরভাগ উদাহরণ ব্রেভিটির জন্য ব্যবহারগুলি বাদ দেবেন। কিন্তু ধন্যবাদ.
ম্যাট জনসন-পিন্ট

4
@MattJohnson ছাড়া @ Saustrup এর বিবৃতি ব্যবহার করে, আপনি কিছু অকেজো কম্পাইল ত্রুটি যেমন পেতে'System.Array' does not contain a definition for 'Where'
জ্যাকব Eggers

7
@ সিলভারসিডডাউন যেমন বলেছে, এটি কেবল নেট 4.5 নিয়ে কাজ করে। আমি Gist.github.com/munr/3544bd7fab6615290561 এ .NET 4.0 এর সাথে সামঞ্জস্যপূর্ণ করতে কিছু এক্সটেনশন তৈরি করেছি । আরেকটি বিষয় লক্ষণীয় হ'ল এটি অনুমানগুলির সাথে কাজ করবে না, কেবলমাত্র সম্পূর্ণ লোড হওয়া সত্তা।
মুন

5
এটি অনুমানের সাথে চলছে সম্পর্কে কোনও পরামর্শ?
জাফিন

33

আমি ম্যাট জনসনের মতামতটি সত্যিই পছন্দ করি তবে আমার মডেলটিতে আমার সমস্ত ডেটটাইম সদস্য হলেন ইউটিসি এবং আমি কোনও বৈশিষ্ট্য সহ তাদের সমস্তকে সাজাইয়া রাখতে চাই না। সুতরাং আমি ইভেন্টটির হ্যান্ডলারটিকে ডিফল্ট কাইন্ড মান প্রয়োগ করার অনুমতি দেওয়ার জন্য ম্যাটের পদ্ধতির সাধারণকরণ করেছি যদি কোনও সদস্য স্পষ্টরূপে অ্যাট্রিবিউট দিয়ে সজ্জিত না হয়।

অ্যাপ্লিকেশনডিবি কনটেক্সট ক্লাসের কনস্ট্রাক্টরটিতে এই কোডটি অন্তর্ভুক্ত রয়েছে:

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

4
এটি গৃহীত উত্তরের জন্য খুব কার্যকর এক্সটেনশন!
শিক্ষার্থী

সম্ভবত আমি কিছু মিস করছি, তবে ডেটটাইমকাইন্ডের বিপরীতে ডেটটাইমকাইন্ড.ইউটিসি'র কাছে এই ডিফল্টটি কীভাবে রয়েছে?
Rhonage

4
@Rhonage এই সম্পর্কে দুঃখিত। ডিফল্টটি অ্যাপ্লিকেশনডিবি কনটেক্সট কনস্ট্রাক্টরে সেট আপ করা হয়েছে। আমি অন্তর্ভুক্ত উত্তর আপডেট।
Bob.at.Indigo.Health

4
@ বব.এট.এআইপসিকল্যাব ধন্যবাদ সাথী, এখন আরও পরিষ্কার। কিছু ওজন প্রতিবিম্ব চলছে কিনা তা বের করার চেষ্টা করছিল - তবে হ্যাঁ, মৃত সরল!
Rhonage

যদি কোনও মডেলটির DateTIme(সর্বজনীন) সেটার পদ্ধতি ব্যতীত কোনও বৈশিষ্ট্য থাকে তবে এটি ব্যর্থ হয় । সম্পাদনা প্রস্তাবিত। এছাড়াও দেখুন স্ট্যাকওভারফ্লো.com
ফ্লোরিয়ান শীত

15

এই উত্তরটি সত্তা ফ্রেমওয়ার্ক 6 এর সাথে কাজ করে

গৃহীত উত্তরটি প্রজেক্টেড বা অজ্ঞাতনামা অবজেক্টের জন্য কাজ করে না। পারফরম্যান্সও সমস্যা হতে পারে।

এটি অর্জনের জন্য, আমাদের একটি ব্যবহার করতে হবে DbCommandInterceptor, এটিটি ফ্রেমওয়ার্ক দ্বারা সরবরাহিত একটি অবজেক্ট।

ইন্টারসেপ্টর তৈরি করুন:

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 DbDataReader, যা আমরা আমাদের দ্বারা প্রতিস্থাপন করি

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

এখনও পর্যন্ত এটি সেরা উত্তরের মতো বলে মনে হচ্ছে। আমি অ্যাট্রিবিউটটির প্রকরণটি প্রথমে চেষ্টা করেছি কারণ এটি কম দূরে পৌঁছেছে তবে আমার ইউনিট পরীক্ষাগুলি উপহাসের সাথে ব্যর্থ হবে কারণ কনস্ট্রাক্টর ইভেন্ট টাই-ইনটি অনমোডেল ক্রিয়েটিং ইভেন্টে ঘটে যাওয়া টেবিল ম্যাপিংগুলি সম্পর্কে জানে না। এই আমার ভোট পায়!
সেনেটর

4
তুমি কেন ছায়া দিচ্ছ Disposeআর GetData?
ব্যবহারকারী 247702

4
এই কোড সম্ভবত উচিত ক্রেডিট @IvanStoev stackoverflow.com/a/40349051/90287
রামি উ:

দুর্ভাগ্যক্রমে এটি ব্যর্থ হয় যদি আপনি স্থানিক ডেটা ম্যাপ করে থাকেন
ক্রিস

@ ইউজার ২7702০২ হ্যাঁ ছায়া ছাঁটা ডিসপোজ ভুল,
ডিসপোজ

12

জন্য মতিন কোর , সেখানে 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);
        }
    }
}

এছাড়াও, আপনি যদি কিছু সংস্থার কিছু সম্পত্তি ইউটিসি হিসাবে বিবেচনা করা থেকে বাদ দিতে চান তবে এই লিঙ্কটি পরীক্ষা করুন check


অবশ্যই আমার জন্য সেরা সমাধান! ধন্যবাদ
বেন মরিস 16

4
@ মারক্রেডম্যান আমার মনে হয় না এটি বোধগম্য হয় কারণ আপনার যদি ডেটটাইমঅফসেটের বৈধ ব্যবহারের মামলা থাকে তবে আপনি সময় অঞ্চল সম্পর্কেও তথ্য রাখতে চান। দেখুন docs.microsoft.com/en-us/dotnet/standard/datetime/... বা stackoverflow.com/a/14268167/3979621 যখন তারিখসময় এবং DateTimeOffset মধ্যে নির্বাচন করার জন্য।
হনজা কালফাস

4
IsQueryTypeদেখে মনে হচ্ছে IsKeyLess: github.com/dotnet/efcore/commit/…
মার্ক

কেন IsQueryType(বা IsKeyLessএখন) চেক দরকার?
পাইটর পেরাক

9

আমি বিশ্বাস করি যে আমি এমন একটি সমাধান খুঁজে পেয়েছি যার জন্য কোনও কাস্টম ইউটিসি চেকিং বা ডেটটাইম ম্যানিপুলেশন প্রয়োজন নেই।

ডেটটাইমঅফসেট (ডেটটাইম নয়) ডেটাটাইপ ব্যবহার করতে মূলত আপনাকে আপনার EF সত্তাগুলি পরিবর্তন করতে হবে। এটি ডাটাবেসে তারিখের মান সহ টাইম জোনটি সঞ্চয় করবে (আমার ক্ষেত্রে এসকিউএল সার্ভার 2015)।

ইএফ কোর যখন ডিবি থেকে ডেটা অনুরোধ করে তখন এটি টাইমজোন সম্পর্কিত তথ্যও গ্রহণ করবে। আপনি যখন কোনও ওয়েব অ্যাপ্লিকেশনে এই ডেটাটি পাস করেন (আমার ক্ষেত্রে অ্যাঙ্গুলার 2) তারিখটি স্বয়ংক্রিয়ভাবে ব্রাউজারের স্থানীয় সময় অঞ্চলে রূপান্তরিত হয় যা আমি প্রত্যাশা করি।

এবং এটি আমার সার্ভারে ফিরে গেলে এটি স্বয়ংক্রিয়ভাবে আবার ইউটিসিতে রূপান্তরিত হয়, প্রত্যাশার মতোও।


8
ডেটটাইম অফসেট সাধারণ উপলব্ধির বিপরীতে সময় অঞ্চল সংরক্ষণ করে না। এটি ইউটিসি থেকে একটি অফসেট সংরক্ষণ করে যা মানটি উপস্থাপন করে। অফসেটটি প্রকৃত সময় অঞ্চলটি অফসেটটি তৈরি করা হয়েছিল তা নির্ধারণ করতে বিপরীতে ম্যাপ করা যায় না, যার ফলে ডেটাটাইপটি প্রায় অকেজো হয়ে যায়।
সানক্যাট 2000

4
না, তবে এটি একটি ডেটটাইম সঠিকভাবে সঞ্চয় করতে ব্যবহার করা যেতে পারে: मध्यम.com
কার্ল

4
কেবল ইউটিসি-র কোনও অবস্থানের প্রয়োজন নেই, কারণ এটি সর্বত্র একই। আপনি যদি ইউটিসি ব্যতীত অন্য কোনও কিছু ব্যবহার করেন তবে আপনারও প্রয়োজন স্থানটির প্রয়োজন, অন্যথায় সময়ের তথ্য অপ্রয়োজনীয়, ডেটটাইমসেট ব্যবহার করার সময়ও।
হরিটসু

4
মূল পোস্টার যা চেয়েছিল তা DATETIMEOFFSET করবে: কোনও (স্পষ্ট) রূপান্তর না করেই ইউটিসি হিসাবে তারিখের সময় সংরক্ষণ করুন store @ কার্ল DATETIME, DATETIME2, এবং DATETIMEOFFSET সমস্ত স্টোরের তারিখ-সময় মান সঠিকভাবে। অতিরিক্তভাবে ইউটিসি থেকে অফসেট সংরক্ষণ করা ছাড়াও, ডেটটাইমএফএফএসইটির মোটামুটি কোনও সুবিধা নেই। আপনি আপনার ডাটাবেসে যা ব্যবহার করেন তা হ'ল আপনার কল। আমি ঠিক এই জায়গায় গাড়ি চালাতে চেয়েছিলাম যে এটি কোনও টাইম জোন সংরক্ষণ করে না যতগুলি অনেকে ভুল করে ভাবেন।
সানক্যাট 2000

4
@ সানকাট 2000 সুবিধাটি হ'ল আপনি এই তারিখটি যেমন আপনার এপি থেকে আপনার ক্লায়েন্ট ব্রাউজারে পাঠাতে পারেন। যখন ক্লায়েন্ট ব্রাউজারটি এই তারিখটি খুলবে তখন এটি জানে যে ইউসিটি থেকে অফসেটটি কী এবং তাই এটি ক্লায়েন্ট এটি দেখছে এমন সিস্টেমে এটি ডিফল্ট তারিখে রূপান্তর করতে সক্ষম। সুতরাং আপনার সার্ভারের সময় অঞ্চল থেকে ব্রাউজারের সময় অঞ্চলে রূপান্তরকরণ বিকাশকারীকে এর জন্য কোনও কোড না লিখেই ঘটে।
মাটোনো

6

আমি এই মুহূর্তে গবেষণা করছি, এবং এই উত্তরগুলির বেশিরভাগই দুর্দান্ত নয়। আমি যা দেখতে পাচ্ছি তা থেকে, EF6 কে বলার উপায় নেই যে ডাটাবেস থেকে আগত তারিখগুলি ইউটিসি ফর্ম্যাটে রয়েছে। যদি এটি হয় তবে আপনার মডেলের ডেটটাইম বৈশিষ্ট্যগুলি ইউটিসি-তে রয়েছে তা নিশ্চিত করার সহজতম উপায় হ'ল সেটারে যাচাই বা রূপান্তর করা।

সিউডোকোডের মতো কিছু সি # এখানে অ্যালগরিদম বর্ণনা করে

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

প্রথম দুটি শাখা সুস্পষ্ট। শেষটি গোপন সস ধরে holds

যখন EF6 ডাটাবেস থেকে লোড হওয়া ডেটা থেকে একটি মডেল তৈরি করে, ডেটটাইমগুলি হয় DateTimeKind.Unspecified। যদি আপনি জানেন যে আপনার তারিখগুলি ডিবিতে সমস্ত ইউটিসি হয়, তবে শেষ শাখাটি আপনার পক্ষে দুর্দান্ত কাজ করবে।

DateTime.Nowসর্বদা হয় DateTimeKind.Local, সুতরাং উপরের অ্যালগরিদম কোডে উত্পন্ন তারিখগুলির জন্য সূক্ষ্ম কাজ করে। অধিকাংশ সময়.

আপনাকে সতর্ক হতে হবে, তবে অন্যান্য কোডগুলিও DateTimeKind.Unspecifiedআপনার কোডটিতে ঝাঁপিয়ে পড়তে পারে। উদাহরণস্বরূপ, আপনি জেএসএন ডেটা থেকে আপনার মডেলগুলি এবং এই জাতীয় ডিভাইসালাইজারের স্বাদের ডিফল্টগুলি ডিসরিয়ালাইজ করতে পারেন। DateTimeKind.Unspecifiedইএফ ছাড়া অন্য কারও কাছ থেকে সেটারে উঠার জন্য চিহ্নিত স্থানীয় তারিখগুলি রক্ষা করা আপনার দায়িত্ব ।


6
আমি এই সমস্যাটি নিয়ে বেশ কয়েক বছর কুস্তি করার পরে জানতে পেরেছি, আপনি যদি অন্য কাঠামোর মধ্যে ডেটটাইম ক্ষেত্রগুলি বরাদ্দ করছেন বা নির্বাচন করছেন, উদাহরণস্বরূপ কোনও ডেটা ট্রান্সফার অবজেক্ট, EF প্রাপ্তকারী এবং সেটার উভয় পদ্ধতি উপেক্ষা করে। এই ক্ষেত্রে, DateTimeKind.Utcআপনার ফলাফল উত্পন্ন হওয়ার পরেও আপনাকে প্রকারের মধ্যে পরিবর্তন করতে হবে। উদাহরণ: from o in myContext.Records select new DTO() { BrokenTimestamp = o.BbTimestamp };সমস্ত ধরনের সেট করে DateTimeKind.Unspecified
সানক্যাট 2000

4
আমি কিছুদিনের জন্য ইন্টিটি ফ্রেমওয়ার্কের সাথে ডেটটাইমঅফসেট ব্যবহার করছি এবং আপনি যদি ডেটটাইমঅফসেটের কোনও টাইপের ডেটা সহ আপনার EF সত্ত্বাগুলি নির্দিষ্ট করে থাকেন তবে আপনার সমস্ত EF অনুসন্ধানগুলি ইউটিসি থেকে অফসেটের সাথে তারিখগুলি ফিরিয়ে দেবে ঠিক যেমন এটি ডিবিতে সংরক্ষণ করা হয়। সুতরাং আপনি যদি ডেটা টাইমের পরিবর্তে আপনার ডেটা টাইপটি ডেটটাইম অফসেটে পরিবর্তন করেন তবে আপনাকে উপরের কাজটির প্রয়োজন হবে না।
মাটোনো

এটা জেনে ভালো লাগলো! ধন্যবাদ @মোটোনো

@ সানক্যাট 2000 মন্তব্য অনুসারে, এটি কেবল কার্যকর হয় না এবং সরানো উচিত
বেন মরিস

5

সত্তা ফ্রেমওয়ার্কে ডেটাটাইমকিন্ড নির্দিষ্ট করার কোনও উপায় নেই। আপনি ডিবায় সংরক্ষণ করার আগে তারিখের সময় মানগুলি ইউটিসি তে রূপান্তর করার সিদ্ধান্ত নিতে পারেন এবং সর্বদা ডিবি থেকে প্রাপ্ত তথ্যটিকে ইউটিসি হিসাবে ধরে নেবেন। তবে ক্যোয়ারির সময় মেটেরালাইজ করা ডেটটাইম অবজেক্টগুলি সর্বদা "অনির্দিষ্ট" থাকবে। আপনি ডেটটাইমের পরিবর্তে ডেটটাইমঅফসেট অবজেক্টটি ব্যবহার করে বিশ্লেষণ করতে পারেন।


5

আর এক বছর, আরেকটি সমাধান! এটি ইএফ কোরের জন্য।

আমার কাছে 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();

বৈশিষ্ট্যগুলির তুলনায় এটির সামান্য সুবিধা রয়েছে যা আপনি কেবল এটি সঠিক ধরণের বৈশিষ্ট্যে প্রয়োগ করতে পারেন।

মনে রাখবেন যে এটি ধরে নেয় ডিবি থেকে মানগুলি ইউটিসিতে রয়েছে তবে কেবল ভুল রয়েছে Kind। অতএব এটি ডিবিতে আপনি যে মানগুলি সংরক্ষণ করার চেষ্টা করছেন তা সূচিত করে, যদি তারা ইউটিসি না হয় তবে বর্ণনামূলক ব্যতিক্রম ছুঁড়ে ফেলে।


4
এটি একটি দুর্দান্ত সমাধান যা উচ্চতর হওয়া উচিত বিশেষত এখন সর্বাধিক নতুন বিকাশ কোর বা নেট ব্যবহার করবে 5.. ইউটিসি প্রয়োগকারী নীতিটির জন্য বোনাসের কাল্পনিক পয়েন্টগুলি - যদি বেশি লোক তাদের তারিখগুলি ইউটিসি রাখে প্রকৃত ব্যবহারকারীর প্রদর্শনে, আমাদের খুব কম তারিখ / সময় বাগ রয়েছে।
oflahero

4

আপনি মানগুলি সেট করার সময় আপনি যদি ইউটিসি তারিখগুলি যথাযথভাবে পাস করতে সাবধান হন এবং সত্তাগুলি ডাটাবেস থেকে পুনরুদ্ধার করার সময় ডেটটাইমকিন্ডটি সঠিকভাবে সেট করা নিশ্চিত করে থাকে তবে আমার উত্তরটি এখানে দেখুন: https://stackoverflow.com/ এ / 9386364/279590


1

আমার মতো .NET ফ্রেমওয়ার্ক 4, প্রতিবিম্ব সিনট্যাক্স / পদ্ধতি সীমাবদ্ধতার সাথে যাদের জন্য @ ম্যাটজহানসন সমাধান অর্জন করতে হবে তাদের নীচে তালিকাভুক্ত হিসাবে এর জন্য কিছুটা পরিবর্তন প্রয়োজন:

     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

ম্যাট জনসন-পিন্টের সমাধানটি কাজ করে, তবে যদি আপনার সমস্ত ডেটটাইমস ইউটিসি হওয়ার কথা হয় তবে একটি বৈশিষ্ট্য তৈরি করা খুব চূড়ান্ত হবে। এখানে আমি এটি কীভাবে সরলীকরণ করেছি:

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

আর একটি পদ্ধতি হ'ল ডেটটাইম বৈশিষ্ট্যগুলির সাথে একটি ইন্টারফেস তৈরি করা, আংশিক সত্তা শ্রেণিতে তাদের প্রয়োগ করা। এবং তারপরে সেভেনচেনজেস ইভেন্টটি ব্যবহার করুন যা বস্তুটি ইন্টারফেসের ধরণের কিনা তা পরীক্ষা করতে, সেই তারিখের মানগুলি যা চান তা সেট করুন। প্রকৃতপক্ষে, যদি এগুলি খাঁটি / পরিবর্তিত ধরণের তারিখগুলি তৈরি করা হয় তবে আপনি সেই ইভেন্টটি তাদের জনপ্রিয় করতে ব্যবহার করতে পারেন।


একটি খারাপ ধারণা নয় তবে ক্লাসগুলি বেনামে বাছাই করা যাবে না।
জন লর্ড

0

আমার ক্ষেত্রে, ইউটিসি ডেটটাইম সহ আমার কেবল একটি টেবিল ছিল। আমি যা করেছি তা এখানে:

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

0

এখানে সমাধানগুলি কার্যকর, তবে আমি আশা করি অনেকেই এই সমস্যাটি নিয়ে আসছেন যে তারা স্থানীয় সময় অঞ্চলটিতে তাদের সমস্ত তারিখের সময় চায় তবে তারা এটি অনুবাদ করতে চায় যাতে স্থির সংস্করণটি ইউটিসি সংরক্ষণ হয়।

এটি বাস্তবায়নের জন্য 3 টি চ্যালেঞ্জ রয়েছে:

  1. ইউটিসি হিসাবে ডেটা পড়া এবং লোকাল রূপান্তর করা
  2. ক্যোয়ারি প্যারামিটারগুলি সমন্বয় করা যেমন পণ্য থেকে বিক্রয় নির্বাচন করুন যেখানে বিক্রয় <@ 1
  3. ডেটা সংরক্ষণ করে যা ইউটিসি হিসাবে লোকালটাইম

1. ডেটাটিকে ইউটিসি হিসাবে পড়া এবং লোকাল রূপান্তর করা

এই ক্ষেত্রে, উপরের সমাধান ইভান স্টয়েভ ডেটটাইম এর কাজের উপর ভিত্তি করে। ডাটাবেস থেকে লোড করার পরে দয়া করে ইউটিসি নয়, অনির্ধারিত সেট করুন আপনার যা প্রয়োজন হবে না।

2. ক্যোয়ারী প্যারামিটারগুলি সামঞ্জস্য করা

ইন্টারসেপ্টারের জন্য ইভানের সমাধান হিসাবে একইভাবে আপনি রিডারএক্সেকুটিং ইন্টারসেপ্টারটি ব্যবহার করতে পারেন। বোনাস হ'ল এটি রিডারঅ্যাক্সিউটেডের চেয়ে প্রয়োগ করা আরও সহজ।

    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        foreach (DbParameter dbParameter in command.Parameters)
        {
            if (dbParameter.Value is DateTime dtLocal)
            {
                if (dtLocal.Kind != DateTimeKind.Utc)
                {
                    dbParameter.Value = dtLocal.ToUniversalTime();
                }
            }
        }
        base.ReaderExecuting(command, interceptionContext);
    }

৩. ডেটা সংরক্ষণ করে যা ইউটিসি হিসাবে লোকালটাইম

যদিও এখানে ক্যোয়ারি ইন্টারসেপ্টর রয়েছে যা তারা এখানে সহায়তা করবে বলে মনে হয়, তাদের একাধিকবার ডাকা হয় এবং অপ্রত্যাশিত ফলাফল তৈরি করা হয়। আমি যে সর্বোত্তম সমাধানটি নিয়ে এসেছি তা হ'ল সেভ চেঞ্জগুলি ওভাররাইড করা

    public override int SaveChanges()
    {
        UpdateCommonProperties();
        UpdateDatesToUtc();
        bool saveFailed;
        do
        {
            saveFailed = false;
            try
            {
                var result = base.SaveChanges();
                return result;
            }
            catch (DbUpdateConcurrencyException ex)
            {
                saveFailed = ConcurrencyExceptionHandler(ex);
            }

        } while (saveFailed);
        return 0;
    }

    private void UpdateDatesToUtc()
    {
        if (!ChangeTracker.HasChanges()) return;

        var modifiedEntries = ChangeTracker.Entries().Where(x => (x.State == EntityState.Added || x.State == EntityState.Modified));

        foreach (var entry in modifiedEntries)
        {
            entry.ModifyTypes<DateTime>(ConvertToUtc);
            entry.ModifyTypes<DateTime?>(ConvertToUtc);
        }
    }

    private static DateTime ConvertToUtc(DateTime dt)
    {
        if (dt.Kind == DateTimeKind.Utc) return dt;
        return dt.ToUniversalTime();
    }

    private static DateTime? ConvertToUtc(DateTime? dt)
    {
        if (dt?.Kind == DateTimeKind.Utc) return dt;
        return dt?.ToUniversalTime();
    }

এবং এক্সটেনশনটি হ'ল (টালন https://stackoverflow.com/a/39974362/618660 এর প্রতিক্রিয়ার ভিত্তিতে)

public static class TypeReflectionExtension
{
    static Dictionary<Type, PropertyInfo[]> PropertyInfoCache = new Dictionary<Type, PropertyInfo[]>();

    static void TypeReflectionHelper()
    {
        PropertyInfoCache = new Dictionary<Type, PropertyInfo[]>();
    }

    public static PropertyInfo[] GetTypeProperties(this Type type)
    {
        if (!PropertyInfoCache.ContainsKey(type))
        {
            PropertyInfoCache[type] = type.GetProperties();
        }
        return PropertyInfoCache[type];
    }

    public static void ModifyTypes<T>(this DbEntityEntry dbEntityEntry, Func<T, T> method)
    {
        foreach (var propertyInfo in dbEntityEntry.Entity.GetType().GetTypeProperties().Where(p => p.PropertyType == typeof(T) && p.CanWrite))
        {
            propertyInfo.SetValue(dbEntityEntry.Entity, method(dbEntityEntry.CurrentValues.GetValue<T>(propertyInfo.Name)));
        }
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.