今天我在代码中找到了一个例外。
这非常令人困惑,因为业务逻辑没有变化,也没有实体更新。经过几个小时的尝试,我决定打开SQL Server分析器。
这是我发现的问题的一个例子。 让我们看看Foo实体定义:
public class Foo
{
public Foo()
{
}
public Foo(int someId, DateTime createdAt, int x, int y)
{
SomeId = someId;
CreatedAt = createdAt;
X = x;
Y = y;
}
public int SomeId { get; private set; }
public DateTime CreatedAt { get; private set; }
public int X { get; private set; }
public int Y { get; private set; }
}
如果您尝试添加Foo实例并保存数据库上下文,它将起作用。
using (var myDbContext = new MyDbContext())
{
DateTime now = DateTime.UtcNow;
var foo = new Foo(1, now, 2, 2);
myDbContext.Set<Foo>().Add(foo);
myDbContext.SaveChanges();
}
以下是保存更改时执行的代码。
exec sp_executesql N'INSERT [dbo].[Foo]([SomeId], [CreatedAt], [X], [Y])
VALUES (@0, @1, @2, @3)
',N'@0 int,@1 datetime2(7),@2 int,@3 int',@0=1,@1='2015-10-15 12:45:15.2580302',@2=2,@3=2
现在让我们添加一个计算列。
public int Sum { get; private set; }
Bellow是迁移中创建列的代码。
Sql("alter table dbo.Foo add Sum as (X + Y)");
更新数据库。现在代码抛出DbUpdateConcurrencyException。这就是原因。如果我们查看SQL Server探查器,我们将看到上面的代码:
exec sp_executesql N'INSERT [dbo].[Foo]([SomeId], [CreatedAt], [X], [Y])
VALUES (@0, @1, @2, @3)
SELECT [Sum]
FROM [dbo].[Foo]
WHERE @@ROWCOUNT > 0 AND [SomeId] = @0 AND [CreatedAt] = @1',N'@0 int,@1 datetime2(7),@2 int,@3 int',@0=1,@1='2015-10-15 12:55:29.4479727',@2=2,@3=2
现在查询返回[Sum]列,因为它是计算的。插入效果很好。但结果集是空的。我认为这会导致DbUpdateConcurrencyException。问题在于@ 1变量的类型。它是datetime2(7),但如果你查看CreatedAt列的类型,你会发现它是datetime。如果您执行上面的脚本,您将在CreatedAt列中找到一个带有“2015-10-15 12:55:29.447”的新行(强制转换正常)。但查询尝试查找CreatedAt等于'2015-10-15 12:55:29.4479727'的行的[Sum]。原因是结果集是空的。
因此,您可以通过两种方式解决问题:
在我的情况下,我先选择,因为我不想更改数据库架构。
Here是重现问题的项目。
PS:对不起我的英文:)
答案 0 :(得分:1)
面对相似的情况我使用以下扩展方法
public static DateTime RoundedToSeconds(this DateTime dt) {
return new DateTime(dt.Ticks - (dt.Ticks % TimeSpan.TicksPerSecond), dt.Kind);
}
那么你的代码应该是
public Foo(int someId, DateTime createdAt, int x, int y)
{
SomeId = someId;
CreatedAt = createdAt.RoundedToSeconds();
X = x;
Y = y;
}
恕我直言,你可以使用舍入到ms。
答案 1 :(得分:1)
此问题不会与当前版本的Entity Framework Core重复。 CreatedAt
的参数类型变为DateTime2
类型。
我认为您错过了提及实体的密钥包含SomeId
和CreatedAt
。
答案 2 :(得分:-1)
这是我发现的问题的一个例子。 让我们看看Foo实体定义:
public class Foo
{
public Foo()
{
}
public Foo(int someId, DateTime createdAt, int x, int y)
{
SomeId = someId;
CreatedAt = createdAt;
X = x;
Y = y;
}
public int SomeId { get; private set; }
public DateTime CreatedAt { get; private set; }
public int X { get; private set; }
public int Y { get; private set; }
}
如果您尝试添加Foo实例并保存数据库上下文,它将起作用。
using (var myDbContext = new MyDbContext())
{
DateTime now = DateTime.UtcNow;
var foo = new Foo(1, now, 2, 2);
myDbContext.Set<Foo>().Add(foo);
myDbContext.SaveChanges();
}
以下是保存更改时执行的代码。
exec sp_executesql N'INSERT [dbo].[Foo]([SomeId], [CreatedAt], [X], [Y])
VALUES (@0, @1, @2, @3)
',N'@0 int,@1 datetime2(7),@2 int,@3 int',@0=1,@1='2015-10-15 12:45:15.2580302',@2=2,@3=2
现在让我们添加一个计算列。
public int Sum { get; private set; }
Bellow是迁移中创建列的代码。
Sql("alter table dbo.Foo add Sum as (X + Y)");
更新数据库。现在代码抛出DbUpdateConcurrencyException。这就是原因。如果我们查看SQL Server探查器,我们将看到上面的代码:
exec sp_executesql N'INSERT [dbo].[Foo]([SomeId], [CreatedAt], [X], [Y])
VALUES (@0, @1, @2, @3)
SELECT [Sum]
FROM [dbo].[Foo]
WHERE @@ROWCOUNT > 0 AND [SomeId] = @0 AND [CreatedAt] = @1',N'@0 int,@1 datetime2(7),@2 int,@3 int',@0=1,@1='2015-10-15 12:55:29.4479727',@2=2,@3=2
现在查询返回[Sum]列,因为它是计算的。插入效果很好。但结果集是空的。我认为这会导致DbUpdateConcurrencyException。问题在于@ 1变量的类型。它是datetime2(7),但如果你查看CreatedAt列的类型,你会发现它是datetime。如果您执行上面的脚本,您将在CreatedAt列中找到一个带有“2015-10-15 12:55:29.447”的新行(强制转换正常)。但查询尝试查找CreatedAt等于'2015-10-15 12:55:29.4479727'的行的[Sum]。原因是结果集是空的。
因此,您可以通过两种方式解决问题:
在我的情况下,我先选择,因为我不想更改数据库架构。
Here是重现问题的项目。
PS:对不起我的英文:)