为什么我的EntityFramework插入这么慢?

时间:2017-07-19 22:37:32

标签: c# entity-framework

我在我的应用程序中发现了一个瓶颈,它是一个特定实体的插入操作(通过导航属性分为三个表)。这些类定义如下:

public class TrackerState
{
    public int Id { get; set; }
    [Index]
    public int TrackerId { get; set; }

    [Index]
    public DateTime DateRecorded { get; set; }
    public DateTime DatePublished { get; set; }
    public DateTime DateReceived { get; set; }
    public LocationStatus LocationStatus { get; set; }
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public double Altitude { get; set; }
    public double Accuracy { get; set; }
    public string Source { get; set; }
    public double Speed { get; set; }
    public double Heading { get; set; }
    public int PrimaryOdometer { get; set; }
    public int SecondaryOdometer { get; set; }
    public int OperationalSeconds { get; set; }
    public virtual IList<AnalogState> AnalogStates { get; set; }
    public virtual IList<DigitalState> DigitalStates { get; set; }
}

public class AnalogState
{
    public int TrackerStateId { get; set; }
    public virtual TrackerState TrackerState { get; set; }
    public int Index { get; set; }
    public int Value { get; set; }
}

public class DigitalState
{
    public int TrackerStateId { get; set; }
    public virtual TrackerState TrackerState { get; set; }
    public int Index { get; set; }
    public bool Value { get; set; }
}

AnalogState和DigitalState类使用TrackerStateId及其索引作为复合主键。

这些表目前非常小:

  • TrackerStates:2719
  • AnalogStates:0
  • DigitalStates:32604

当我通过SQL管理工作室手动插入表格时,操作会在几分之一秒内完成。当我通过实体框架插入时,使用以下代码,最多可能需要15秒,并且所花费的时间量非常依赖于跟踪器状态中包含的数字值的数量 - 例如具有0个数字值的跟踪器状态需要0.1到0.5秒,具有64个数字值的跟踪器状态需要10到15秒。

public async Task<int> AddAsync(TrackerState trackerState)
{
    using (var context = ContextFactory.CreateContext())
    {
        context.TrackerStates.Add(trackerState);
        await context.SaveChangesAsync();
        return trackerState.Id;
    }
}

基于此,似乎实体框架在后台做了很慢的事情,但我无法弄清楚原因。考虑到这种情况的发生频率,0.5秒的交易速度相当慢。 15秒太慢了。到目前为止我尝试过的事情都没有成功:

  • 禁用更改跟踪。我并不期望这样做很多,因为无论如何我都会为每笔交易使用单独的上下文。
  • 首先插入跟踪器状态,然后在单独的步骤中插入数字状态。无论如何,实体框架可能在内部执行此操作。

更新1

我正在使用EntityFramework 6.1.3。我无法弄清楚如何查看正在执行的SQL,但我更新了存储库的存储方法以使用SQL而不是EF:

context.Database.ExecuteSqlCommand("INSERT INTO DigitalStates ([TrackerStateId], [Index], [Value]) VALUES (@Id, @Index, @Value)",
                    new SqlParameter("Id", entity.Id),
                    new SqlParameter("Index", digital.Index),
                    new SqlParameter("Value", digital.Value));

这部分仅占大部分时间。插入7个条目需要3秒钟。

1 个答案:

答案 0 :(得分:1)

在一次交易中保存所有数字状态产生了巨大的差异:

if (trackerState.DigitalStates.Count > 0)
{
    var query = "INSERT INTO DigitalStates ([TrackerStateId], [Index], [Value]) VALUES "
        + string.Join(",", trackerState.DigitalStates.Select(state => String.Format("({0}, {1}, {2})", entity.Id, state.Index, state.Value ? 1 : 0)));
    context.Database.ExecuteSqlCommand(query);
}

出于某种原因,让实体框架自动添加集合似乎是为数据库添加了添加的每个数字状态的请求,尽管我的印象是它应该是一个事务,由上下文触发{ {1}}方法。此修复程序已将其从线性时间更改为大约恒定时间,相对于集合的大小。现在我的下一个问题是,为什么