2013-01-09 5 views
7

Sto salvando un oggetto Cart nel database che ha un datetime nullable. Questo è l'errore ottengo:Perché il codice EF5 utilizza prima datetime2 quando si inserisce un datetime nullable nel database?

La conversione di un tipo di dati datetime2 un tipo di dati datetime determinato un valore out-of-range.

Ci sono alcuni post Stackoverflow che documentano le correzioni a questo problema. Tuttavia, quando il codice crea per la prima volta il database, creerà il campo come DateTime (consentire i valori null). Ma per qualche ragione, il codice tenta prima di inserirlo usando un campo DateTime2.

Mi chiedo perché EF crea il campo in un modo, ma inserisce un diverso tipo per lo stesso campo.

Questo è l'oggetto di dominio:

using System; 
using System.Collections.Generic; 

namespace Core.Domain.Cart 
{ 
    public partial class Cart : BaseEntity, ILocalizedEntity 
    { 
     private ICollection<Catalog> _catalogs; 

     /// <summary> 
     /// Gets or sets the name 
     /// </summary> 
     public virtual string Name { get; set; } 

     /// <summary> 
     /// Gets or sets the zone identifier 
     /// </summary> 
     public virtual int ZoneId { get; set; } 

     /// <summary> 
     /// Gets or sets the brand identifier 
     /// </summary> 
     public virtual int BrandId { get; set; } 

     /// <summary> 
     /// Gets or sets the customer type identifier 
     /// </summary> 
     public virtual int CustomerTypeId { get; set; } 

     /// <summary> 
     /// Gets or sets the date and time of the opening of a cart 
     /// </summary> 
     public virtual DateTime? OpeningDateUtc { get; set; } 

     /// <summary> 
     /// Gets or sets the date and time of the closing of a cart 
     /// </summary> 
     public virtual DateTime? ClosingDateUtc { get; set; } 

     /// <summary> 
     /// Gets or sets a value indicating whether the entity is online or not 
     /// </summary> 
     public virtual bool IsOnline { get; set; } 

     /* Truncated for relevance */ 
    }  
} 

Il modello:

using FluentValidation.Attributes; 
using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
using System.Web.Mvc; 
using Telerik.Web.Mvc; 


namespace Admin.Models.Cart 
{ 
     [Validator(typeof(CartValidator))] 
     public partial class CartModel : BaseNopEntityModel, ILocalizedModel<CartLocalizedModel> 
     {    
      public CartModel() 
      { 
       Locales = new List<CartLocalizedModel>(); 
       Catalogs = new List<CatalogModel>(); 
       UnassociatedCatalogs = new List<CatalogModel>(); 
      } 
      [NopResourceDisplayName("Admin.Carts.Fields.Name")] 
      [AllowHtml] 
      public string Name { get; set; } 

      //Zone dropdown 
      [NopResourceDisplayName("Admin.Carts.Fields.ZoneList")] 
      public SelectList ZoneList { get; set; }  //The dropdown with zones 
      public int ZoneId { get; set; }     //The selected value of the dropdown once the form is submitted 
      public string ZoneName { get; set; }   //The name of the zone to display in data-grid List view. 

      //Brand dropdown 
      [NopResourceDisplayName("Admin.Carts.Fields.BrandList")] 
      public SelectList BrandList { get; set; }  //The dropdown with brands 
      public int BrandId { get; set; }    //The selected value of the dropdown once the form is submitted 
      public string BrandName { get; set; }   //The name of the brand to display in the data-grid List view. 

      //Customer type dropdown 
      [NopResourceDisplayName("Admin.Carts.Fields.CustomerTypeList")] 
      public SelectList CustomerTypeList { get; set; }//The dropdown with CustomerType 
      public int CustomerTypeId { get; set; }   //The selected value of the dropdown once the form is submitted 
      public string CustomerTypeName { get; set; } //The name of the CustomerType to display in the data-grid List view. 

      [NopResourceDisplayName("Admin.Carts.Fields.OpeningDateUtc")] 
      [UIHint("DateNullable")] 
      public DateTime? OpeningDateUtc { get; set; } 

      [NopResourceDisplayName("Admin.Carts.Fields.ClosingDateUtc")] 
      [UIHint("DateNullable")] 
      public DateTime? ClosingDateUtc { get; set; } 

      [NopResourceDisplayName("Admin.Carts.Fields.IsOnline")] 
      public bool IsOnline { get; set; } 

      /* Truncated for relevance */ 
     } 

} 

Così sia il OpeningDateUtc e ClosingDateUtc sono del tipo DateTime ?.

In questo modo il database viene generato dal codice EF prima: EF generated table

Il OpeningDateUtc e ClosingDateUtc sono creati come un campo DateTime nullable.

Allora perché è quando salvo utilizzando il IDBContext.SaveChanges(), l'SQL generato per la query è:

exec sp_executesql N'update [dbo].[Cart] 
set [Name] = @0, [ZoneId] = @1, [BrandId] = @2, [CustomerTypeId] = @3, [OpeningDateUtc] = @4, [ClosingDateUtc] = @5, [IsOnline] = @6, [IsReadonly] = @7, [IsPreviewMode] = @8, [CreatedOnUtc] = @9 
where ([Id] = @10) 
',N'@0 nvarchar(100),@1 int,@2 int,@3 int,@4 datetime2(7),@5 datetime2(7),@6 bit,@7 bit,@8 bit,@9 datetime2(7),@10 int',@0=N'Cart1',@1=7,@2=4,@3=5,@4='2013-01-09 00:00:00',@5='2013-01-18 00:00:00',@6=0,@7=0,@8=1,@9='0001-01-01 00:00:00',@10=1 

La parte interessante è @4 datetime2(7),@5 datetime2(7).

Capisco che potrei risolvere questo problema aggiungendo un .HasColumnType("datetime2") alla mappa del carrello, ma non risponde perché EF5 (e probabilmente vecchie versioni) li imposta su datetime nullable.

risposta

15

Il tipo DateTime in .NET ha lo stesso intervallo e la precisione di datetime2 in SQL Server. Quando EF inserisce o aggiorna una colonna datetime o datetime2 in SQL Server, converte la proprietà del modello nel tipo che può contenere l'intero intervallo di DateTime in .NET, ovvero datetime2. La conversione in datetime non riuscirebbe se la proprietà DateTime non si trova nell'intervallo di datetime in SQL Server.

Il problema che causa l'eccezione sono, tra l'altro, non i due nullable OpeningDateUtc e ClosingDateUtc colonne, ma il valore CreatedOnUtc che è '0001-01-01 00:00:00' nel tuo snippet SQL, vale a dire CreatedOnUtc non è apparentemente inizializzato nella vostra entità del modello. La data più recente in cui è possibile memorizzare datetime in SQL Server è nell'anno 1750, quindi l'anno 0001 non si adatta al tipo (ma si adatterà a datetime2).

Quindi, la soluzione è di impostare CreatedOnUtc su un valore valido datetime oppure - come è noto - definire i tipi come datetime2 nella mappatura.

Ma concordo, ci sarebbe meno confusione se EF mappasse le proprietà DateTime per impostazione predefinita su datetime2.

+0

Grazie, ho una soluzione. –

11

Il team EF ha discusso di questo particolare oggetto durante una delle riunioni di progettazione. La decisione era di lasciare il comportamento attuale così com'è. Ecco gli meeting notes che possono darti più contesto.

+0

Ottima lettura, grazie! –

+0

@MattR Grazie per l'informazione –