Entity Framework, update multiple fields more efficiently

时间:2017-04-06 16:55:18

标签: sql entity-framework sql-update

Using Entity Framework, I am updating about 300 rows, and 9 columns about every 30 seconds. Below is how I am currently doing it. My question is, how can I make the code more efficient?

Every once in a while, I feel my database gets hit with the impact and I just want to make it as efficient as possible.

// FOREACH OF MY 300 ROWS
var original = db.MarketDatas.FirstOrDefault(x => x.BBSymbol == targetBBsymbol);

if (original != null)
{
    //if (original.BBSymbol.ToUpper() == "NOH7 INDEX")
    //{
    //    var x1 = 1;
    //}

    original.last_price = marketDataItem.last_price;
    original.bid = marketDataItem.bid;
    original.ask = marketDataItem.ask;

    if (marketDataItem.px_settle_last_dt_rt != null)
    {
        original.px_settle_last_dt_rt = marketDataItem.px_settle_last_dt_rt;
    }

    if (marketDataItem.px_settle_actual_rt != 0)
    {
        original.px_settle_actual_rt = marketDataItem.px_settle_actual_rt;
    }

    original.chg_on_day = marketDataItem.chg_on_day;

    if (marketDataItem.prev_close_value_realtime != 0)
    {
        original.prev_close_value_realtime = marketDataItem.prev_close_value_realtime;
    }

    if (marketDataItem.px_settle_last_dt_rt != null)
    {
        DateTime d2 = (DateTime)marketDataItem.px_settle_last_dt_rt;

        if (d1.Day == d2.Day)
        {
            //market has settled
            original.settled = "yes";
        }
        else
        {
            //market has NOT settled
            original.settled = "no";
        }
    }

    if (marketDataItem.updateTime.Year != 1)
    {
        original.updateTime = marketDataItem.updateTime;
    }

    db.SaveChanges();
}

Watching what is being hit in the debugger...

SELECT TOP (1) 
[Extent1].[MarketDataID] AS [MarketDataID], 
[Extent1].[BBSymbol] AS [BBSymbol], 
[Extent1].[Name] AS [Name], 
[Extent1].[fut_Val_Pt] AS [fut_Val_Pt], 
[Extent1].[crncy] AS [crncy], 
[Extent1].[fut_tick_size] AS [fut_tick_size], 
[Extent1].[fut_tick_val] AS [fut_tick_val], 
[Extent1].[fut_init_spec_ml] AS [fut_init_spec_ml], 
[Extent1].[last_price] AS [last_price], 
[Extent1].[bid] AS [bid], 
[Extent1].[ask] AS [ask], 
[Extent1].[px_settle_last_dt_rt] AS [px_settle_last_dt_rt], 
[Extent1].[px_settle_actual_rt] AS [px_settle_actual_rt], 
[Extent1].[settled] AS [settled], 
[Extent1].[chg_on_day] AS [chg_on_day], 
[Extent1].[prev_close_value_realtime] AS [prev_close_value_realtime], 
[Extent1].[last_tradeable_dt] AS [last_tradeable_dt], 
[Extent1].[fut_notice_first] AS [fut_notice_first], 
[Extent1].[updateTime] AS [updateTime]
FROM [dbo].[MarketDatas] AS [Extent1]
WHERE ([Extent1].[BBSymbol] = @p__linq__0) OR (([Extent1].[BBSymbol] IS NULL) AND (@p__linq__0 IS NULL))

It seems it updates the same thing multiple times, if I am understanding it correctly.

UPDATE [dbo].[MarketDatas]
SET [last_price] = @0, [chg_on_day] = @1, [updateTime] = @2
WHERE ([MarketDataID] = @3)

UPDATE [dbo].[MarketDatas]
SET [last_price] = @0, [chg_on_day] = @1, [updateTime] = @2
WHERE ([MarketDataID] = @3)

3 个答案:

答案 0 :(得分:3)

您可以将此减少到2次往返。

  1. 不要在循环中调用SaveChanges()。将它移到外面并在完成所有处理后调用它。
  2. 以这样的方式编写选择,即它一次性检索所有原件并将它们推送到内存集合,然后从中检索您正在更新/插入的每个项目。
  3. <强>码

    // use this as your source
    // to retrieve an item later use TryGetValue
    var originals = db.MarketDatas
      .Where(x => arrayOftargetBBsymbol.Contains(x.BBSymbol));
      .ToDictionary(x => x.BBSymbol, y => y);
    
    // iterate over changes you want to make
    foreach(var change in changes){
        MarketData original = null;
    
        // is there an existing entity
        if(originals.TryGetValue(change.targetBBsymbol, out original)){
            // update your original
        }
    }
    
    // save changes all at once
    db.SaveChanges();
    

答案 1 :(得分:1)

You could only execute "db.SaveChanges" after your foreach loop. It think it you would do exactly what your are asking for.

答案 2 :(得分:1)

It seems it updates the same thing multiple times, if I am understanding it correctly.

Entity Framework performs a database round-trip for every entity to update.

Just check the parameter value, they will be different.

how can I make the code more efficient

The major problem is your current solution is not scalable.

It works well when you only have a few entities to update but will become worse and worse are the number of items to update in a batch will increase.

It's often better to make this kind of logic all in the database, but perhaps you cannot do it.


Disclaimer: I'm the owner of the project Entity Framework Extensions

This library can make your code more efficient by allowing you to save multiples entities at once. All bulk operations are supported:

  • BulkSaveChanges
  • BulkInsert
  • BulkUpdate
  • BulkDelete
  • BulkMerge
  • BulkSynchronize

Example:

// Easy to use
context.BulkSaveChanges();

// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);

// Perform Bulk Operations
context.BulkDelete(customers);
context.BulkInsert(customers);
context.BulkUpdate(customers);

// Customize Primary Key
context.BulkMerge(customers, operation => {
   operation.ColumnPrimaryKeyExpression = 
        customer => customer.Code;
});