วิธีแก้ไขข้อผิดพลาดการแปลง datetime2 นอกช่วงโดยใช้ DbContext และ SetInitializer


138

ฉันใช้ DbContext และ Code First API ที่แนะนำกับ Entity Framework 4.1

ข้อมูลแบบใช้ชนิดข้อมูลพื้นฐานเช่นและstring DateTimeคำอธิบายประกอบข้อมูลเดียวที่ฉันใช้ในบางกรณีคือ[Required]แต่ไม่ได้อยู่ในDateTimeคุณสมบัติใด ๆ ตัวอย่าง:

public virtual DateTime Start { get; set; }

subclass DbContextยังเป็นที่เรียบง่ายและดูเหมือนว่า:

public class EventsContext : DbContext
{
    public DbSet<Event> Events { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Event>().ToTable("Events");
    }
}

ตัวเริ่มต้นกำหนดวันที่ในแบบจำลองเป็นค่าที่เหมาะสมในปีนี้หรือปีหน้า

อย่างไรก็ตามเมื่อฉันเรียกใช้ initializer ฉันได้รับข้อผิดพลาดนี้ที่context.SaveChanges():

การแปลงชนิดข้อมูล datetime2 เป็นชนิดข้อมูลวันที่และเวลาส่งผลให้ค่าอยู่นอกช่วง คำสั่งยุติแล้ว

ฉันไม่เข้าใจว่าทำไมสิ่งนี้จึงเกิดขึ้นเพราะทุกอย่างเรียบง่ายมาก ฉันยังไม่แน่ใจว่าจะแก้ไขอย่างไรเนื่องจากไม่มีไฟล์ edmx ให้แก้ไข

ความคิดใด ๆ ?


3
คุณสามารถใช้ SQL Profiler เพื่อดูแทรก / อัพเดตคำสั่ง SQL ได้หรือไม่? ค่อนข้างยากที่จะบอกว่าเกิดอะไรขึ้นที่นี่ - เราไม่เห็นตัวเริ่มต้นหรือเอนทิตีของคุณ SQL Profiler จะช่วยคุณได้มากในการแปลปัญหา
Ladislav Mrnka

1
ในกรณีของฉันฉันได้เพิ่มฟิลด์ลงในตารางและแก้ไขแบบฟอร์มลืมอัปเดต Bind Includes และฟิลด์ของฉันได้รับการตั้งค่าเป็น NULL ดังนั้นข้อผิดพลาดจึงช่วยแก้ไขการกำกับดูแลของฉัน
strattonn

คำตอบ:


190

คุณต้องแน่ใจว่า Start มากกว่าหรือเท่ากับ SqlDateTime.MinValue (1 มกราคม 1753) - โดยค่าเริ่มต้น Start เท่ากับ DateTime.MinValue (1 มกราคม 0001)


14
ฉันได้ทิ้งออบเจ็กต์ initializer บางส่วนไว้โดยไม่ได้กำหนดวันที่ดังนั้นมันจะเป็นค่าเริ่มต้นเป็น DateTime.MinValue
Alex Angas

ฉันก็ทำเช่นนั้นเหมือนกัน ^. เพิ่มฟิลด์วันที่ที่กำหนดเองลงในอ็อบเจ็กต์ ApplicationUser เอกลักษณ์ของ asp.net จากนั้นลืมที่จะเริ่มต้นเป็นสิ่งที่เหมาะสม (
ไมค์ Devenney

ในกรณีของฉันปัญหาคือในวันที่ต่ำสุด 01/01/0001 กำลังสร้างข้อผิดพลาด
Machado

ฉันจะตรวจสอบ dateTime.minvalue ได้อย่างไร
Anabeil

1
ช้าไปหน่อย แต่ @Anabeil คุณน่าจะทำได้แค่ Console.WriteLine (DateTime.MinValue) ในหน้าต่าง VS / linqpad
SIRHAMY

22

เรียบง่าย ในรหัสของคุณก่อนอื่นให้ตั้งค่าประเภท DateTime เป็น DateTime? ดังนั้นคุณสามารถทำงานกับประเภท DateTime ที่เป็นโมฆะในฐานข้อมูลได้ ตัวอย่างเอนทิตี:

public class Alarme
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public DateTime? DataDisparado { get; set; }//.This allow you to work with nullable datetime in database.
        public DateTime? DataResolvido { get; set; }//.This allow you to work with nullable datetime in database.
        public long Latencia { get; set; }

        public bool Resolvido { get; set; }

        public int SensorId { get; set; }
        [ForeignKey("SensorId")]
        public virtual Sensor Sensor { get; set; }
    }

6
นั่นไม่ใช่กรณีเสมอไป {1/1/0001 00:00:00 AM} ยังทำให้เกิดข้อผิดพลาดนี้
eran otzap

20

ในบางกรณีDateTime.MinValue(หรือเทียบเท่าdefault(DateTime)) ถูกใช้เพื่อระบุค่าที่ไม่รู้จัก

วิธีการต่อแบบง่ายๆนี้สามารถช่วยจัดการสถานการณ์ดังกล่าวได้:

public static class DbDateHelper
{
    /// <summary>
    /// Replaces any date before 01.01.1753 with a Nullable of 
    /// DateTime with a value of null.
    /// </summary>
    /// <param name="date">Date to check</param>
    /// <returns>Input date if valid in the DB, or Null if date is 
    /// too early to be DB compatible.</returns>
    public static DateTime? ToNullIfTooEarlyForDb(this DateTime date)
    {
        return (date >= (DateTime) SqlDateTime.MinValue) ? date : (DateTime?)null;
    }
}

การใช้งาน:

 DateTime? dateToPassOnToDb = tooEarlyDate.ToNullIfTooEarlyForDb();

13

คุณสามารถทำให้ฟิลด์ว่างเปล่าได้หากเหมาะสมกับข้อกังวลในการสร้างแบบจำลองของคุณ วันที่ว่างจะไม่ถูกบังคับให้เป็นวันที่ที่ไม่อยู่ในช่วงของ SQL DateTime ให้พิมพ์ตามที่ค่าเริ่มต้นจะเป็น อีกทางเลือกหนึ่งคือการจับคู่ประเภทอื่นอย่างชัดเจนโดยอาจใช้

.HasColumnType("datetime2")

12

แม้ว่าคำถามนี้จะค่อนข้างเก่าและมีคำตอบที่ดีอยู่แล้ว แต่ฉันคิดว่าควรใส่อีกหนึ่งข้อซึ่งจะอธิบายถึง 3 แนวทางที่แตกต่างกันในการแก้ปัญหานี้

แนวทางที่ 1

อย่างชัดเจน map DateTimeคุณสมบัติpublic virtual DateTime Start { get; set; }ที่จะdatetime2อยู่ในคอลัมน์ที่สอดคล้องกันในตาราง เพราะโดยปกติจะ EF datetimeแผนที่ไปยัง

สามารถทำได้โดยใช้ API ที่คล่องแคล่วหรือคำอธิบายประกอบข้อมูล

  1. API ที่คล่องแคล่ว

    ในคลาส DbContext จะแทนที่OnModelCreatingและกำหนดค่าคุณสมบัติStart(สำหรับคำอธิบายเหตุผลที่เป็นคุณสมบัติของคลาส EntityClass)

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Configure only one property 
        modelBuilder.Entity<EntityClass>()
            .Property(e => e.Start)
            .HasColumnType("datetime2");
    
       //or configure all DateTime Preperties globally(EF 6 and Above)
        modelBuilder.Properties<DateTime>()
            .Configure(c => c.HasColumnType("datetime2"));
    }
    
  2. คำอธิบายประกอบข้อมูล

    [Column(TypeName="datetime2")]
    public virtual DateTime Start { get; set; }
    

แนวทางที่ 2

เริ่มต้นStartเป็นค่าเริ่มต้นในตัวสร้าง EntityClass ซึ่งเป็นการดีที่หากStartไม่ได้ตั้งค่าด้วยเหตุผลบางประการก่อนบันทึกเอนทิตีลงในการเริ่มต้นฐานข้อมูลจะมีค่าเริ่มต้นเสมอ ตรวจสอบให้แน่ใจว่าค่าเริ่มต้นมากกว่าหรือเท่ากับSqlDateTime.MinValue (ตั้งแต่ 1 มกราคม 1753 ถึง 31 ธันวาคม 9999)

public class EntityClass
{
    public EntityClass()
    {
        Start= DateTime.Now;
    }
    public DateTime Start{ get; set; }
}

แนวทางที่ 3

ทำให้Startเป็นประเภท nullable DateTime -note ?หลังDateTime-

public virtual DateTime? Start { get; set; }

สำหรับคำอธิบายเพิ่มเติมโปรดอ่านโพสต์นี้


8

หากDateTimeคุณสมบัติของคุณเป็นโมฆะในฐานข้อมูลให้แน่ใจว่าได้ใช้DateTime?สำหรับคุณสมบัติอ็อบเจ็กต์ที่เกี่ยวข้องมิฉะนั้น EF จะส่งผ่านDateTime.MinValueสำหรับค่าที่ไม่ได้กำหนดซึ่งอยู่นอกช่วงของสิ่งที่ประเภทวันเวลาและเวลาของ SQL สามารถจัดการได้


8

วิธีแก้ปัญหาของฉันคือเปลี่ยนคอลัมน์วันที่และเวลาทั้งหมดเป็น datetime2 และใช้ datetime2 สำหรับคอลัมน์ใหม่ กล่าวอีกนัยหนึ่งทำให้ EF ใช้ datetime2 โดยค่าเริ่มต้น เพิ่มสิ่งนี้ในเมธอด OnModelCreating บนบริบทของคุณ:

modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));

ที่จะได้รับ DateTime และ DateTime ทั้งหมด? คุณสมบัติในเอนทิตีทั้งหมดของคุณ


3

เริ่มต้นคุณสมบัติ Start ในตัวสร้าง

Start = DateTime.Now;

สิ่งนี้ใช้ได้ผลสำหรับฉันเมื่อฉันพยายามเพิ่มฟิลด์ใหม่สองสามฟิลด์ลงในตารางผู้ใช้ของ ASP .Net Identity Framework (AspNetUsers) โดยใช้ Code First ฉันอัปเดต Class - ApplicationUser ใน IdentityModels.cs และฉันเพิ่มฟิลด์ lastLogin ประเภท DateTime

public class ApplicationUser : IdentityUser
    {
        public ApplicationUser()
        {
            CreatedOn = DateTime.Now;
            LastPassUpdate = DateTime.Now;
            LastLogin = DateTime.Now;
        }
        public String FirstName { get; set; }
        public String MiddleName { get; set; }
        public String LastName { get; set; }
        public String EmailId { get; set; }
        public String ContactNo { get; set; }
        public String HintQuestion { get; set; }
        public String HintAnswer { get; set; }
        public Boolean IsUserActive { get; set; }

        //Auditing Fields
        public DateTime CreatedOn { get; set; }
        public DateTime LastPassUpdate { get; set; }
        public DateTime LastLogin { get; set; }
    }

2

ตามคำตอบของ user @ andygjp จะดีกว่าถ้าคุณแทนที่Db.SaveChanges()วิธีการพื้นฐานและเพิ่มฟังก์ชันเพื่อแทนที่วันที่ใด ๆ ที่ไม่อยู่ระหว่าง SqlDateTime.MinValue และ SqlDateTime.MaxValue

นี่คือโค้ดตัวอย่าง

public class MyDb : DbContext
{
    public override int SaveChanges()
    {
        UpdateDates();
        return base.SaveChanges();
    }

    private void UpdateDates()
    {
        foreach (var change in ChangeTracker.Entries().Where(x => (x.State == EntityState.Added || x.State == EntityState.Modified)))
        {
            var values = change.CurrentValues;
            foreach (var name in values.PropertyNames)
            {
                var value = values[name];
                if (value is DateTime)
                {
                    var date = (DateTime)value;
                    if (date < SqlDateTime.MinValue.Value)
                    {
                        values[name] = SqlDateTime.MinValue.Value;
                    }
                    else if (date > SqlDateTime.MaxValue.Value)
                    {
                        values[name] = SqlDateTime.MaxValue.Value;
                    }
                }
            }
        }
    }
}

นำมาจากความคิดเห็นของผู้ใช้ @ sky-dev บนhttps://stackoverflow.com/a/11297294/9158120


1

ฉันมีปัญหาเดียวกันและในกรณีของฉันฉันกำลังตั้งวันที่เป็น DateTime ใหม่ () แทน DateTime.Now


0

ในกรณีของฉันสิ่งนี้เกิดขึ้นเมื่อฉันใช้เอนทิตีและตาราง sql มีค่าเริ่มต้นของ datetime == getdate () สิ่งที่ฉันทำเพื่อตั้งค่าให้กับฟิลด์นี้


0

ฉันใช้ฐานข้อมูลก่อนและเมื่อข้อผิดพลาดนี้เกิดขึ้นกับฉันวิธีแก้ปัญหาของฉันคือบังคับ ProviderManifestToken = "2005" ในไฟล์ edmx (ทำให้โมเดลเข้ากันได้กับ SQL Server 2005) ไม่ทราบว่าสิ่งที่คล้ายกันนี้เป็นไปได้สำหรับ Code First หรือไม่


0

หนึ่งบรรทัดแก้ไขสิ่งนี้:

modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));

ดังนั้นในรหัสของฉันฉันจึงเพิ่ม:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}

การเพิ่มหนึ่งบรรทัดในคลาสย่อย DBContext แทนที่ void OnModelCreating ส่วนควรใช้งานได้


0

ในกรณีของฉันหลังจากการปรับโครงสร้างใหม่ใน EF6 การทดสอบของฉันล้มเหลวด้วยข้อความแสดงข้อผิดพลาดเดียวกันกับโปสเตอร์ต้นฉบับ แต่วิธีแก้ปัญหาของฉันไม่เกี่ยวข้องกับฟิลด์ DateTime

ฉันแค่ไม่มีฟิลด์บังคับเมื่อสร้างเอนทิตี เมื่อฉันเพิ่มช่องที่หายไปข้อผิดพลาดก็หายไป หน่วยงานของฉันมี DateTime สองวันหรือไม่ แต่ก็ไม่ใช่ปัญหา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.