实体框架Database.ExecuteSqlCommand随机更新删除

时间:2017-05-03 08:24:27

标签: .net sql-server entity-framework-6

我正在使用EF6 Database.ExecuteSqlCommand在数据库中执行一些原始SQL更新,这似乎是一个非常简单和直接的任务。但是,有几次更新语句不会随机执行。该应用程序每天都在积极使用,一天内可以插入/更新超过10,000行,但有两次ONE更新似乎没有运行,有一次有大约20次更新似乎没有运行。

考虑以下用例:

名为Trip的模型,同一组中的每个Trip都需要拥有前一个Trip和下一个Trip的外键。

需要将大约5000次旅行插入数据库。为了导入这些Trips,我需要尽快插入它们,所以我使用SqlBulkCopy来完成任务。因此,我无法在插入期间填充外键,因此我将不得不在之后更新每一行。为此,我在批量复制完成后从数据库中加载所有行程,然后遍历它们并执行更新以设置外键。

代码如下所示:

// ... after SqlBulkCopy is done
string tripGroup = null;
Trip previousTrip = null;

foreach (Trip trip in trips)
{
    if (tripGroup != trip.TripGroup)
    {
        tripGroup = trip.TripGroup;
        previousTrip = null;
    }

    if (previousTrip != null)
    {
        uow.Context.Database.ExecuteSqlCommand("UPDATE [Trips] SET PrevTripId = {0} WHERE Id = {1}", previousTrip.Id, trip.Id);
        uow.Context.Database.ExecuteSqlCommand("UPDATE [Trips] SET NextTripId = {0} WHERE Id = {1}", trip.Id, previousTrip.Id);
    }
    previousTrip = trip;
}

表格如下所示:

enter image description here

问题是有时PrevTripId和NextTripId不会被填充。这只发生在服务器上,我无法在我的本地开发机器上重现这个问题。值得指出的是,在生产中,应用程序服务器和sql server是分开的。我尝试将代码放在循环中运行50次,同时不断进行数据库备份,运行选择等,但我没有运气。

我现在唯一能想到的就是监控结果并做一些日志记录,以便我可以看到它何时会发生。但我必须等到它再次发生。

非常感谢任何可能导致问题的原因或有关如何重新创建/调试此问题的任何建议。

1 个答案:

答案 0 :(得分:0)

ORM不适合批处理操作,无论这些操作涉及批量加载还是批量更新。此代码绕过ORM本身每行发送2个UPDATE语句,导致2N操作 plus 加载内存中的所有数据。

使用SQL执行批处理操作要简单得多,速度更快,更安全。一些SQL语句可以更新单个事务中的所有行,而无需在客户端上加载任何内容。

在这种情况下,您可以利用LAGLEAD分析函数来检索分区中的上一个/下一个值。

假设旅行按ID排序并由TripGroup分区,您可以使用以下方式检索上一个/下一个ID:

select 
    id,
    LAG(ID,1,null) OVER (Partition by TripGroup order by id) as PrevTripID,
    LEAD(ID,1,null) OVER (Partition by TripGroup order by id) as NextTripId
from Trips;

这将返回与图片中发布的结果相同的结果。

要更新Trips,您需要使用CTE,因为分析函数只能在SELECT语句中使用:

with ids as (
select 
    id,
    LAG(ID,1,null) OVER (Partition by TripGroup order by id) as PrevTripID,
    LEAD(ID,1,null) OVER (Partition by TripGroup order by id) as NextTripId
from Trips
)
update t
set
    PevTripId =ids.PrevTripID,
    NextTripId =ids.NextTripId 
from Trips t inner join ids on ids.Id=t.id;

您只需要一个语句,而不是加载5000行并执行10000个命令。