簡體   English   中英

使用實體框架 Codefirst 存儲 TimeSpan - SqlDbType.Time 溢出

[英]Storing TimeSpan with Entity Framework Codefirst - SqlDbType.Time overflow

我試圖在我的數據庫中植入一些常量:

context.Stages.AddOrUpdate(s => s.Name,
                                   new Stage()
                                   {
                                       Name = "Seven",
                                       Span = new TimeSpan(2, 0, 0),
                                       StageId = 7
                                   });
context.Stages.AddOrUpdate(s => s.Name,
                                   new Stage()
                                   {
                                       Name = "Eight",
                                       Span = new TimeSpan(1, 0, 0, 0),
                                       StageId = 8
                                   });

這在我用於 EF Codefirst 遷移的 Seed() 函數中。 它在第八階段失敗,原因如下:

System.Data.UpdateException:更新條目時出錯。 有關詳細信息,請參閱內部異常。 ---> System.OverflowException: SqlDbType.Time 溢出。 值“1.00:00:00”超出范圍。 必須介於 00:00:00.0000000 和 23:59:59.9999999 之間。

為什么我不能使用 EF 存儲時間跨度? 我真的希望我不需要在這里兩端做一些愚蠢的時間到滴答轉換......

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Obsolete("Property '" + nameof(Duration) + "' should be used instead.")]        
    public long DurationTicks { get; set; }

    [NotMapped]
    public TimeSpan Duration
    {
#pragma warning disable 618
      get { return new TimeSpan(DurationTicks); }
      set { DurationTicks = value.Ticks; }
#pragma warning restore 618
    }

更新

從 EF Core 2.1 開始,現在可以使用Value Conversion實現這一點。

builder.Entity<Stage>()
    .Property(s => s.Span)
    .HasConversion(new TimeSpanToTicksConverter()); // or TimeSpanToStringConverter

在兩端進行時間到滴答轉換不再是愚蠢的。 不確定他們何時添加它,但實體框架現在將選擇適當的內置轉換器(如果存在)(在本例中為 TimeSpanToTicksConverter)。 您需要做的就是向實體類添加一個屬性,實體框架將自動為 SQL 表中的列提供與 TimeSpan 類相同的范圍。

public class Stage
{
    public string Name { get; set; }

    [Column(TypeName = "bigint")]
    public TimeSpan Span { get; set; }

    public int StageId { get; set; }
}

我確信 bigint 不是 TimeSpan 的默認列類型,以實現人類可讀性和向后兼容性,但這似乎是一個非常完美的解決方案。

我希望這可以幫助任何人在六年后遇到這個問題。

文檔: https : //docs.microsoft.com/en-us/ef/core/modeling/value-conversions

在這一行:

Span = new TimeSpan(1, 0, 0, 0)

您正在使用此構造函數:

public TimeSpan(int days, int hours, int minutes, int seconds);

因此,您實際上創建了一個大於 24 小時的TimeSpan因為您將1傳遞給days參數,而您的基礎數據庫類型是Time ,它只接受 00:00-23:59 之間的值。

很難判斷您是否真的打算使用 1 天的TimeSpan ,或者這只是一個錯字。

如果您真的想要超過 24 小時的TimeSpan ,我想您必須將您的字段映射到另一種數據庫類型(如SmallDateTime )。

如果這只是一個錯字錯誤,只需將您的行更改為:

Span = new TimeSpan(1, 0, 0),

如前所述,問題在於 EF 將 TimeSpan 類映射到 Time,時間限制為 24 小時。

如果您需要存儲超過 24 小時的時間跨度,我建議使用以下兩種方法之一:

1)為時間跨度的不同元素創建一個具有 int 屬性的 TimeSpan 實體,例如:

 public class Timespan
{
    public Int64 Id { get; set; }

    public Int16 Years { get; set; }

    public int Months { get; set; }

    public Int64 Days { get; set; }

    public Int64 Hours { get; set; }

    public Int64 Minutes { get; set; }
}

只需將適用實體中的外部引用添加到您的自定義 Timespan 實體即可。

2)做一些愚蠢的時間-滴答轉換,如這篇文中所述。

現在 EF Core 有一個內置的ticks <=> TimeSpan轉換。 您所要做的就是:

builder.Property(p => p.SomeTimeSpanProperty)
    .HasConversion<long>();

無需實例化新的TimeSpanToTicksConverter或任何東西。 只要指定類型long作為一般的參數,你就大功告成了。

有關詳細信息,請參閱EF Core 內置值轉換器

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM