Làm cách nào để sửa lỗi chuyển đổi ngoài phạm vi datetime2 bằng DbContext và SetInitializer?


136

Tôi đang sử dụng API DbContext và Code First được giới thiệu với Entity Framework 4.1.

Các mô hình dữ liệu sử dụng các kiểu dữ liệu cơ bản như stringDateTime. Chú thích dữ liệu duy nhất tôi đang sử dụng trong một số trường hợp là [Required], nhưng đó không phải là bất kỳ DateTimethuộc tính nào. Thí dụ:

public virtual DateTime Start { get; set; }

Lớp con DbContext cũng đơn giản và trông giống như:

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

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

Trình khởi tạo đặt ngày trong mô hình thành các giá trị hợp lý trong năm nay hoặc năm sau.

Tuy nhiên, khi tôi chạy trình khởi chạy, tôi gặp lỗi này tại context.SaveChanges():

Việc chuyển đổi kiểu dữ liệu datetime2 thành kiểu dữ liệu datetime dẫn đến giá trị ngoài phạm vi. Các tuyên bố này đã bị chấm dứt.

Tôi không hiểu tại sao điều này lại xảy ra bởi vì mọi thứ rất đơn giản. Tôi cũng không chắc chắn cách khắc phục vì không có tệp edmx để chỉnh sửa.

Có ý kiến ​​gì không?


2
Bạn có thể sử dụng SQL Profiler để xem các câu lệnh SQL chèn / cập nhật không? Thật khó để nói những gì đang diễn ra ở đây - chúng tôi không thấy trình khởi tạo hoặc thực thể của bạn. SQL Profiler sẽ giúp bạn rất nhiều để bản địa hóa vấn đề.
Ladislav Mrnka

1
Trong trường hợp của tôi, tôi đã thêm một trường vào bảng và biểu mẫu chỉnh sửa, quên cập nhật Bind Bao gồm và trường của tôi đã được đặt thành NULL. Vì vậy, lỗi đã giúp sửa chữa sự giám sát của tôi.
strattonn

Câu trả lời:


186

Bạn phải đảm bảo rằng Bắt đầu lớn hơn hoặc bằng SqlDateTime.MinValue (ngày 1 tháng 1 năm 1753) - theo mặc định Bắt đầu bằng DateTime.MinValue (ngày 1 tháng 1 năm 0001).


14
Tôi đã để lại một số đối tượng trình khởi tạo của mình mà không có ngày đã đặt, vì vậy nó sẽ được mặc định là DateTime.MinValue.
Alex Angas

Tôi cũng đã làm điều đó ^. Đã thêm trường ngày tùy chỉnh vào đối tượng ứng dụng nhận dạng asp.net sau đó quên khởi tạo nó thành một cái gì đó có ý nghĩa. : (
Mike Devenney

Trong trường hợp của tôi, vấn đề là vào ngày tối thiểu, 01/01/0001 đã tạo ra lỗi.
Machado

Làm thế nào tôi có thể kiểm tra dateTime.minvalue?
Anabeil

1
Hơi muộn một chút, nhưng @Anabeil bạn sẽ có thể chỉ Console.WriteLine (DateTime.MinValue) trong cửa sổ ngay lập tức của VS / linqpad
SIRHAMY

22

Đơn giản. Trước tiên, trên mã của bạn, hãy đặt loại DateTime thành DateTime?. Vì vậy, bạn có thể làm việc với loại DateTime nullable trong cơ sở dữ liệu. Ví dụ thực thể:

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
không phải lúc nào cũng như vậy {1/1/0001 12:00:00 AM} cũng khiến lỗi này xuất hiện
eran otzap

20

Trong một số trường hợp, DateTime.MinValue(hoặc tương đương,default(DateTime) ) được sử dụng để chỉ ra một giá trị không xác định.

Phương pháp mở rộng đơn giản này có thể giúp xử lý các tình huống như vậy:

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

Sử dụng:

 DateTime? dateToPassOnToDb = tooEarlyDate.ToNullIfTooEarlyForDb();

13

Bạn có thể làm cho trường không thể, nếu phù hợp với mối quan tâm mô hình cụ thể của bạn. Một ngày null sẽ không bị ép buộc vào một ngày không nằm trong phạm vi của kiểu SQL DateTime theo cách mà một giá trị mặc định sẽ làm. Một tùy chọn khác là ánh xạ rõ ràng sang một loại khác, có thể với,

.HasColumnType("datetime2")

12

Mặc dù câu hỏi này khá cũ và đã có câu trả lời tuyệt vời, tôi nghĩ tôi nên đặt thêm một câu giải thích 3 cách tiếp cận khác nhau để giải quyết vấn đề này.

Cách tiếp cận thứ 1

Một cách rõ ràng bản đồ DateTimebất động sản public virtual DateTime Start { get; set; }để datetime2ở cột tương ứng trong bảng. Bởi vì theo mặc định, EF sẽ ánh xạ nó tới datetime.

Điều này có thể được thực hiện bằng API thông thạo hoặc chú thích dữ liệu.

  1. API thông thạo

    Trong lớp DbContext, lớp phủ OnModelCreatingvà cấu hình thuộc tính Start(vì lý do giải thích đó là thuộc tính của lớp 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. Chú thích dữ liệu

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

Cách tiếp cận thứ 2

Khởi tạo Startmột giá trị mặc định trong hàm tạo EntityClass. Điều này tốt vì nếu vì một lý do nào đó, giá trị của Startkhông được đặt trước khi lưu thực thể vào cơ sở dữ liệu bắt đầu sẽ luôn có giá trị mặc định. Đảm bảo giá trị mặc định lớn hơn hoặc bằng SqlDateTime.MinValue (từ ngày 1 tháng 1 năm 1753 đến ngày 31 tháng 12 năm 9999)

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

Cách tiếp cận thứ 3

Làm Startcho loại nullable DateTime -note ? sau DateTime-

public virtual DateTime? Start { get; set; }

Để giải thích thêm đọc bài này


8

Nếu các DateTimethuộc tính của bạn là null trong cơ sở dữ liệu thì hãy chắc chắn sử dụng DateTime?cho các thuộc tính đối tượng được liên kết hoặc EF sẽ truyền vào DateTime.MinValuecho các giá trị chưa được gán nằm ngoài phạm vi của loại thời gian SQL có thể xử lý.


8

Giải pháp của tôi là chuyển tất cả các cột datetime sang datetime2 và sử dụng datetime2 cho bất kỳ cột mới nào. Nói cách khác, làm cho EF sử dụng datetime2 theo mặc định. Thêm phần này vào phương thức OnModelCreating trong ngữ cảnh của bạn:

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

Điều đó sẽ nhận được tất cả DateTime và DateTime? thuộc tính trên tất cả các thực thể của bạn.


3

khởi tạo thuộc tính Start trong constructor

Start = DateTime.Now;

Điều này hoạt động với tôi khi tôi đang cố gắng thêm một vài trường mới vào bảng Người dùng của Khung nhận dạng ASP .Net (AspNetUsers) bằng Code First. Tôi đã cập nhật Class - ApplicationUser trong IdentityModels.cs và tôi đã thêm một trường LastLogin loại 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

Dựa trên câu trả lời của người dùng @ andygjp, sẽ tốt hơn nếu bạn ghi đè lên cơ sở Db.SaveChanges() phương thức và thêm một hàm để ghi đè bất kỳ ngày nào không nằm giữa SqlDateTime.MinValue và SqlDateTime.MaxValue.

Đây là mã mẫu

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

Lấy từ nhận xét của người dùng @ sky-dev trên https://stackoverflow.com/a/11297294/9158120


1

Tôi gặp vấn đề tương tự và trong trường hợp của tôi, tôi đã đặt ngày thành DateTime mới () thay vì DateTime. Bây giờ


0

Trong trường hợp của tôi, điều này xảy ra khi tôi sử dụng thực thể và bảng sql có giá trị mặc định là datetime == getdate (). Vì vậy, những gì tôi đã làm để thiết lập một giá trị cho lĩnh vực này.


0

Tôi đang sử dụng Cơ sở dữ liệu Đầu tiên và khi lỗi này xảy ra với tôi, giải pháp của tôi là bắt buộc CarrierManifestToken = "2005" trong tệp edmx (làm cho các mô hình tương thích với SQL Server 2005). Không biết nếu một cái gì đó tương tự có thể cho Code First.


0

Một dòng sửa lỗi này:

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

Vì vậy, trong mã của tôi, tôi đã thêm:

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

Thêm một dòng đó vào lớp con DBContext ghi đè khoảng trống Phần OnModelCreating sẽ hoạt động.


0

Trong trường hợp của tôi, sau một số lần tái cấu trúc trong EF6, các thử nghiệm của tôi đã thất bại với cùng một thông báo lỗi như poster gốc nhưng giải pháp của tôi không liên quan gì đến các trường DateTime.

Tôi chỉ thiếu một trường bắt buộc khi tạo thực thể. Khi tôi thêm trường bị thiếu, lỗi sẽ biến mất. Thực thể của tôi có hai DateTime? các lĩnh vực nhưng chúng không phải là vấn đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.