简体   繁体   English

使用实体框架 Codefirst 存储 TimeSpan - SqlDbType.Time 溢出

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

I'm trying to seed some constants into my DB:我试图在我的数据库中植入一些常量:

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

This is within my Seed() function for EF Codefirst Migrations.这在我用于 EF Codefirst 迁移的 Seed() 函数中。 It fails at Stage Eight with the following:它在第八阶段失败,原因如下:

System.Data.UpdateException: An error occurred while updating the entries. System.Data.UpdateException:更新条目时出错。 See the inner exception for details.有关详细信息,请参阅内部异常。 ---> System.OverflowException: SqlDbType.Time overflow. ---> System.OverflowException: SqlDbType.Time 溢出。 Value '1.00:00:00' is out of range.值“1.00:00:00”超出范围。 Must be between 00:00:00.0000000 and 23:59:59.9999999.必须介于 00:00:00.0000000 和 23:59:59.9999999 之间。

Why would I not be able to store a timespan using EF?为什么我不能使用 EF 存储时间跨度? I really hope I don't need to do some silly time-to-ticks conversion on both ends here...我真的希望我不需要在这里两端做一些愚蠢的时间到滴答转换......

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

Update更新

This is now achievable since EF Core 2.1, using Value Conversion .从 EF Core 2.1 开始,现在可以使用Value Conversion实现这一点。

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

Doing a time-to-ticks conversion on both ends is no longer silly.在两端进行时间到滴答转换不再是愚蠢的。 Not sure when they added it, but Entity Framework will now select the appropriate built in converter if one exists (in this case TimeSpanToTicksConverter).不确定他们何时添加它,但实体框架现在将选择适当的内置转换器(如果存在)(在本例中为 TimeSpanToTicksConverter)。 All you need to do is add a single attribute to your entity class and Entity Framework will automagically give the column in the SQL table the same range as the TimeSpan class.您需要做的就是向实体类添加一个属性,实体框架将自动为 SQL 表中的列提供与 TimeSpan 类相同的范围。

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

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

    public int StageId { get; set; }
}

I'm sure bigint isn't the default column type for TimeSpan for human readability and backwards compatibility, but this seems like a pretty much perfect solution.我确信 bigint 不是 TimeSpan 的默认列类型,以实现人类可读性和向后兼容性,但这似乎是一个非常完美的解决方案。

I hope this helps anybody experiencing this issue six years later.我希望这可以帮助任何人在六年后遇到这个问题。

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

In this line:在这一行:

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

You're using this constructor:您正在使用此构造函数:

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

So you're actually creating a TimeSpan greater than 24 hours since you're passing 1 to the days parameter, while your underlying Database type is Time which only accepts values between 00:00-23:59.因此,您实际上创建了一个大于 24 小时的TimeSpan因为您将1传递给days参数,而您的基础数据库类型是Time ,它只接受 00:00-23:59 之间的值。

Hard to tell whether you actually meant to have a TimeSpan with 1 day, or it's just a typo.很难判断您是否真的打算使用 1 天的TimeSpan ,或者这只是一个错字。

If you really want a TimeSpan greater than 24 hours, i guess you'll have to map your field to another Database type (like SmallDateTime ).如果您真的想要超过 24 小时的TimeSpan ,我想您必须将您的字段映射到另一种数据库类型(如SmallDateTime )。

If it's just a typo error, just change your line to:如果这只是一个错字错误,只需将您的行更改为:

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

The problem, as previously mentioned, is the fact that EF maps the TimeSpan class to Time, which is limited to 24 hours.如前所述,问题在于 EF 将 TimeSpan 类映射到 Time,时间限制为 24 小时。

If you need to store a timespan of greater than 24 hours, I would suggest one of the following two approaches:如果您需要存储超过 24 小时的时间跨度,我建议使用以下两种方法之一:

1) Create a TimeSpan entity with int properties for the different elements of a timespan, something like: 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; }
}

Simply add a foreign reference in the applicable entity to your custom Timespan entity.只需将适用实体中的外部引用添加到您的自定义 Timespan 实体即可。

2) Do some silly time-to-ticks conversion, as explained in this blog post . 2)做一些愚蠢的时间-滴答转换,如这篇文中所述。

Now EF Core has a built-in ticks <=> TimeSpan conversion.现在 EF Core 有一个内置的ticks <=> TimeSpan转换。 All you have to do is:您所要做的就是:

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

No need to instantiate a new TimeSpanToTicksConverter or anything.无需实例化新的TimeSpanToTicksConverter或任何东西。 Just specify the type long as the generic argument, and you're done.只要指定类型long作为一般的参数,你就大功告成了。

For more info see EF Core Built-in Value Converters有关详细信息,请参阅EF Core 内置值转换器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM