Entity Framework 7 插入日期时间失败

Entity Framework 7 fails to insert datetime

我正在使用 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 上的单声道下失败。同SQLserver/database。生成的 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 上的失败是:

Conversion failed when converting date and/or time from character string.

问题

  1. 为什么 Windows 将类型显式设置为 datetime 时生成 datetime2
  2. linux 版本正在生成无效的 SQL,因此失败。如何在 mono/linux 上插入 datetime 值?
  1. .NET DateTime 类型比 sql 服务器的 "datetime" 类型范围更广。实际上,.NET DateTime 与 sql 服务器的 "datetime2" 类型具有相同的范围,这就是为什么 Entity Framework 在将 DateTime 转换为 sql 日期(就像在你的例子中一样)。在这种情况下,table 列的类型无关紧要。您可以阅读此 design meeting notes,其中 EF 团队讨论了 datetime 和 datetime2 的问题并决定保持原样(以及背后的原因)。

  2. Mono 使用 TDS 与 sql 服务器一起工作(基于 FreeTDS,并且它使用相当过时的版本。这个版本不知道 sql 服务器的 "datetime2" 类型,因此没有升级到新版本的 TDS(也许现在工作量太大),而是实施了某种 hack,转换 datetime2 from\to 字符串。现在,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 服务器一起使用。那么,您可以通过在 EF 模型中将 ProviderManifestToken 设置为 2005 来解决问题。这将使 EF 与 sql 服务器 2005 一起工作,并且不会在所有地方使用 datetime2。但是您显然会丢失 sql 服务器 2005 之后添加的其他类型,更不用说那是肮脏的 hack。

附带说明 - 最好不要在 sql 服务器上对单声道进行任何认真的开发。 Sql mono 中的服务器提供程序充满了错误,上面的错误是最无辜的错误之一。它有空值问题,连接池问题(这个问题非常严重 - 如果你在连接池连接上超时 - 这个连接将在整个生命周期内中断并且 ALL 查询连接将失败。Hack - 如果您的查询在连接池上超时,则从连接池中删除连接)等。大多数此类错误已为人所知多年,但仍未修复。如果您有选择 - 只需使用 postgresql,它是免费的,很好,而且我在单声道上没有遇到任何问题。