【问题标题】:Entity Framework 7 fails to insert datetimeEntity Framework 7 无法插入日期时间
【发布时间】:2015-12-26 08:06:31
【问题描述】:

我正在使用 EntityFramework 7,beta7 并具有以下实体:

C#

public class Log
{
    [Column(TypeName ="datetime")]
    public DateTime Date { get; set; }
    ...
}

SQL

CREATE TABLE [dbo].[Logs] (
    [Date] [datetime] NOT NULL,
    ...
)

我执行以下操作:

db.Logs.Add(new Log { Date = DateTime.UtcNow });
db.SaveChanges();

这在 Windows 上是成功的,但在 debian 上的 mono 下失败。相同的 SQL 服务器/数据库。生成的 SQL 如下。注意@p1 渲染的类型和值的区别:

Windows

exec sp_executesql N'SET NOCOUNT OFF;
INSERT INTO [Logs] ([Browser], [Date], [Exception], [HostAddress], [Level], [Logger], [Message], [Thread], [Url], [Username])
OUTPUT INSERTED.[Id]
VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9);
',N'@p0 nvarchar(max) ,@p1 datetime2(7),@p2 nvarchar(max) ,@p3 nvarchar(max) ,@p4 nvarchar(4000),@p5 nvarchar(4000),@p6 nvarchar(4000),@p7 nvarchar(4000),@p8 nvarchar(max) ,@p9 nvarchar(max) ',@p0=NULL,@p1='2015-09-28 23:02:26.0367851',@p2=NULL,@p3=NULL,@p4=N'INFO',@p5=N'Fanatics.ConsoleApp.Program',@p6=N'Console app test',@p7=N'0',@p8=NULL,@p9=NULL
go

Linux

exec sp_executesql N'SET NOCOUNT OFF;
INSERT INTO [Logs] ([Browser], [Date], [Exception], [HostAddress], [Level], [Logger], [Message], [Thread], [Url], [Username])
OUTPUT INSERTED.[Id]
VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9);
',N'@p0 nvarchar(4000), @p1 char(27), @p2 nvarchar(4000), @p3 nvarchar(4000), @p4 nvarchar(4000), @p5 nvarchar(4000), @p6 nvarchar(4000), @p7 nvarchar(4000), @p8 nvarchar(4000), @p9 nvarchar(4000)',@p0=NULL,@p1='2015-09-28T23:03:21.5561720',@p2=NULL,@p3=NULL,@p4=N'INFO',@p5=N'Fanatics.ConsoleApp.Program',@p6=N'Console app test',@p7=N'0',@p8=NULL,@p9=NULL
go

Linux 上的失败是:

从字符串转换日期和/或时间时转换失败。

问题

  1. 当类型显式设置为 datetime 时,为什么 Windows 会生成 datetime2
  2. linux 版本生成无效SQL,因此失败。如何在 mono/linux 上插入 datetime 值?

【问题讨论】:

  • 您使用的是最新的 beta 7 吗?

标签: sql-server windows mono debian entity-framework-core


【解决方案1】:
  1. .NET DateTime 类型比 sql server 的“datetime”类型范围更广。实际上,.NET DateTime 与 sql server 的“datetime2”类型具有相同的范围,这就是为什么 Entity Framework 在将 DateTime 转换为 sql 日期(如您的示例中)时会在任何地方使用 datetime2(如果可能)。在这种情况下,表列的类型无关紧要。您可以阅读此design meeting notes,其中 EF 团队讨论了 datetime 和 datetime2 的问题并决定保持原样(以及其背后的原因)。

  2. Mono 使用 TDS 与 sql server 一起工作(基于FreeTDS,它使用的是相当过时的版本。这个版本不知道 sql server 的 "datetime2" 类型,所以不要升级到新版本的 TDS (也许现在工作太多了), 实现了一种 hack, 将 datetime2 从\转换为字符串. 现在, datetime2 比 datetime 具有更高的精度, 所以在将 datetime2 转换为字符串然后将该字符串转换为 datetime 时(这是隐式完成的) 导致您看到的错误。这很容易检查:

    select cast('2015-09-28T23:03:21.5561720' as datetime2) -- < all fine
    select cast('2015-09-28T23:03:21.5561720' as datetime) -- < error from your question
    

    您可以阅读更多关于它的信息here

您可能会问是否可以在单声道上将 EF 与 sql server 一起使用。好吧,您可以通过在 EF 模型中将 ProviderManifestToken 设置为 2005 来解决问题。这将使 EF 可以与 sql server 2005 一起使用,并且不会在任何地方使用 datetime2。但是你显然会丢失 sql server 2005 之后添加的其他类型,更不用说是脏 hack。

附带说明 - 最好不要使用 sql server 对 mono 进行任何认真的开发。 Mono 中的 Sql server provider 充满了 bug,而上面的 bug 是最无辜的一个。它有空值问题,连接池问题(这个问题非常严重 - 如果您在连接池连接上超时 - 此连接将在整个生命周期内中断,并且该连接上的 ALL 查询将失败. Hack - 如果您的查询超时,则从连接池中删除连接)等。大多数此类错误已存在多年且仍未修复。如果您有选择 - 只需使用 postgresql,它是免费的、很好的,而且我在 mono 上没有任何问题。

【讨论】:

  • 同意@evk。 Mono + SQL Server 是一个糟糕的组合。 .NET Core 可能是未来更好的解决方案。
  • 我切换到npgsql,没有问题了
  • 现在已经两年多了。任何解决方案@Evk?
  • @mugumedavid 是的 - 不再需要单声道(对于这种类型的项目) - 使用 .net 核心和实体框架核心,它与 linux 上的 sql server 配合得很好。
  • 谢谢,完美运行。尽管依赖关系发生了很大变化,并且对于现有的 MVC 项目来说可能非常麻烦。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-31
  • 2016-05-06
  • 2021-03-22
  • 2016-03-28
  • 1970-01-01
  • 2014-10-24
相关资源
最近更新 更多