Jak naprawić błąd konwersji spoza zakresu datetime2 za pomocą DbContext i SetInitializer?


136

Używam interfejsów API DbContext i Code First wprowadzonych w Entity Framework 4.1.

Model danych wykorzystuje podstawowe typy danych, takie jak stringi DateTime. Jedyna adnotacja danych, której używam w niektórych przypadkach, to [Required], ale nie ma jej w żadnej z DateTimewłaściwości. Przykład:

public virtual DateTime Start { get; set; }

DbContext podklasą jest również prosty i wygląda następująco:

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

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

W initializer zestawy dat w modelu do wartości sensownych w obu tego roku lub w przyszłym roku.

Jednak po uruchomieniu inicjatora pojawia się ten błąd pod adresem context.SaveChanges():

Konwersja typu danych datetime2 na typ danych datetime spowodowała uzyskanie wartości spoza zakresu. Oświadczenie zostało zakończone.

W ogóle nie rozumiem, dlaczego tak się dzieje, ponieważ wszystko jest takie proste. Nie jestem też pewien, jak to naprawić, ponieważ nie ma pliku edmx do edycji.

Jakieś pomysły?


2
Czy możesz używać programu SQL Profiler do wyświetlania instrukcji wstawiania / aktualizowania instrukcji SQL? Trudno powiedzieć, co się tutaj dzieje - nie widzimy Twojego inicjatora ani jednostek. SQL Profiler bardzo pomoże Ci zlokalizować problem.
Ladislav Mrnka

1
W moim przypadku dodałem pole do tabeli i formularza edycji, zapomniałem zaktualizować Bind Includes i moje pole było ustawione na NULL. Więc błąd pomógł naprawić mój niedopatrzenie.
strattonn

Odpowiedzi:


186

Musisz upewnić się, że Start jest większy lub równy SqlDateTime.MinValue (1 stycznia 1753) - domyślnie Start jest równy DateTime.MinValue (1 stycznia 0001).


14
Pozostawiłem niektóre z moich obiektów inicjalizujących bez ustawionej daty, więc domyślnie byłby to DateTime.MinValue.
Alex Angas

Ja też to zrobiłem ^. Dodano niestandardowe pole daty do obiektu ApplicationUser tożsamości asp.net, a następnie zapomniałem zainicjować go do czegoś, co miało sens. : (
Mike Devenney

W moim przypadku problem tkwił w dacie min, 01/01/0001 generował błąd.
Machado

jak mogę sprawdzić dateTime.minvalue?
Anabeil

1
Trochę późno, ale @Anabeil powinieneś być w stanie po prostu Console.WriteLine (DateTime.MinValue) w bezpośrednim oknie VS / linqpad
SIRHAMY

22

Prosty. W kodzie najpierw ustaw typ DateTime na DateTime ?. Możesz więc pracować z typem DateTime dopuszczającym wartość null w bazie danych. Przykład jednostki:

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
nie zawsze tak jest. Również ten błąd pojawia się w przypadku {1/1/0001 12:00:00 AM}
eran otzap

20

W niektórych przypadkach DateTime.MinValue(lub równoważniedefault(DateTime) ) służy do wskazania nieznanej wartości.

Ta prosta metoda rozszerzenia może pomóc w takich sytuacjach:

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

Stosowanie:

 DateTime? dateToPassOnToDb = tooEarlyDate.ToNullIfTooEarlyForDb();

13

Możesz ustawić wartość pola na wartość null, jeśli odpowiada to Twoim konkretnym problemom związanym z modelowaniem. Data pusta nie zostanie wymuszona na datę, która nie mieści się w zakresie typu SQL DateTime, tak jak byłaby to wartość domyślna. Inną opcją jest jawne odwzorowanie na inny typ, na przykład za pomocą

.HasColumnType("datetime2")

12

Chociaż to pytanie jest dość stare i istnieją już świetne odpowiedzi, pomyślałem, że powinienem podać jeszcze jedno, które wyjaśnia 3 różne podejścia do rozwiązania tego problemu.

Podejście 1

Jawnie przypisz DateTimewłaściwość public virtual DateTime Start { get; set; }do datetime2w odpowiedniej kolumnie w tabeli. Ponieważ domyślnie EF zamapuje go na datetime.

Można to zrobić za pomocą płynnego interfejsu API lub adnotacji danych.

  1. Fluent API

    W klasie DbContext nadpisanie OnModelCreatingi skonfigurowanie właściwości Start(ze względów wyjaśnienia jest to właściwość klasy 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. Adnotacja danych

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

Podejście 2

Zainicjuj Startdo wartości domyślnej w konstruktorze EntityClass.To jest dobre, jakby z jakiegoś powodu wartość Startnie została ustawiona przed zapisaniem jednostki do bazy danych start zawsze będzie miał wartość domyślną. Upewnij się, że wartość domyślna jest większa lub równa SqlDateTime.MinValue (od 1 stycznia 1753 do 31 grudnia 9999)

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

Trzecie podejście

Make Startto be of type nullable DateTime -note ? after DateTime-

public virtual DateTime? Start { get; set; }

Więcej wyjaśnień znajdziesz w tym poście


8

Jeśli DateTimewłaściwości dopuszczają wartość null w bazie danych, pamiętaj, aby użyć DateTime?dla skojarzonych właściwości obiektu lub EF zostanie przekazany DateTime.MinValuedla nieprzypisanych wartości, które są poza zakresem tego, co może obsłużyć typ daty / godziny SQL.


8

Moim rozwiązaniem było przełączenie wszystkich kolumn z datą i godziną na datetime2 i użycie datetime2 dla wszystkich nowych kolumn. Innymi słowy, spraw, aby EF domyślnie używał datetime2. Dodaj to do metody OnModelCreating w swoim kontekście:

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

To dostanie wszystkie DateTime i DateTime? właściwości wszystkich Twoich podmiotów.


3

zainicjuj właściwość Start w konstruktorze

Start = DateTime.Now;

To zadziałało, gdy próbowałem dodać kilka nowych pól do tabeli użytkowników ASP .Net Identity Framework (AspNetUsers) przy użyciu Code First. Zaktualizowałem Class - ApplicationUser w IdentityModels.cs i dodałem pole lastLogin typu 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

Opierając się na odpowiedzi użytkownika @ andygjp, lepiej jest zastąpić podstawę Db.SaveChanges() metodę i dodać funkcję nadpisującą dowolną datę, która nie mieści się między SqlDateTime.MinValue i SqlDateTime.MaxValue.

Oto przykładowy kod

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

Zaczerpnięte z komentarza użytkownika @ sky-dev na https://stackoverflow.com/a/11297294/9158120


1

Miałem ten sam problem iw moim przypadku ustawiałem datę na new DateTime () zamiast DateTime.


0

W moim przypadku stało się tak, gdy użyłem encji, a tabela sql ma domyślną wartość datetime == getdate (). więc co zrobiłem, aby ustawić wartość w tym polu.


0

Używam Database First i kiedy pojawił się ten błąd, moim rozwiązaniem było wymuszenie ProviderManifestToken = "2005" w pliku edmx (dzięki czemu modele są zgodne z SQL Server 2005). Nie wiem, czy coś podobnego jest możliwe w przypadku Code First.


0

Jedna linia rozwiązuje ten problem:

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

Tak więc w moim kodzie dodałem:

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

Dodanie tego wiersza do podklasy DBContext przesłania void OnModelCreating sekcja powinno działać.


0

W moim przypadku po refaktoryzacji w EF6 moje testy zakończyły się niepowodzeniem z tym samym komunikatem o błędzie, co oryginalny plakat, ale moje rozwiązanie nie miało nic wspólnego z polami DateTime.

Po prostu brakowało mi wymaganego pola podczas tworzenia encji. Po dodaniu brakującego pola błąd zniknął. Mój podmiot ma dwie daty i godziny? pola, ale to nie był problem.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.