C#实体框架:批量扩展输入内存问题

时间:2020-08-17 22:16:42

标签: c# .net entity-framework asp.net-core entity-framework-extensions

我目前正在使用EF扩展程序。我不明白的一件事,“它应该有助于提高性能”

但是,将一百万条以上的记录放入List变量中,本身就是内存问题。 因此,如果要更新百万条记录而又不将所有内容保存在内存中,那么如何有效地做到这一点?

我们应该使用library(shiny) library(shinyjs) #Define ui first_module_ui <- function(id) { ns <- NS(id) tagList(numericInput( inputId = ns("first_input"), label = "First input:", value = 1 )) } #Define server logic first_module_server <- function(input, output, session) { return(input) } #Define ui second_module_ui <- function(id) { ns <- NS(id) tagList(uiOutput(outputId = ns("second_input")), numericInput( inputId = ns("additional_input"), label = "Additional input", value = 5 )) } #Define server logic second_module_server <- function(input, output, session, first_module_res) { ns <- session$ns second_input <- reactive({ first_module_res$first_input + 1 }) output$second_input <- renderUI({ disabled(textInput( inputId = ns("second_input"), label = "Second input:", value = second_input() )) }) return(list( second_input = reactive({second_input()}), additional_input = reactive({input$additional_input}) )) } #Define ui third_module_ui <- function(id) { ns <- NS(id) tagList(uiOutput(outputId = ns("third_input")), verbatimTextOutput(outputId = ns("fourth_output"))) } #Define server logic third_module_server <- function(input, output, session, second_module_res) { ns <- session$ns third_input <- reactive({ second_module_res$second_input() + 1 }) output$third_input <- renderUI({ disabled(textInput( inputId = ns("third_input"), label = "Third input:", value = third_input() )) }) output$fourth_output <- renderPrint({ second_module_res$additional_input() }) } # Define UI ui <- fluidPage( useShinyjs(), # Application title titlePanel("Demo"), # Sidebar sidebarLayout( sidebarPanel( first_module_ui("first") ), mainPanel( second_module_ui("second"), third_module_ui("third") ) ) ) # Define server logic server <- function(input, output, session) { first_module_res <- callModule(first_module_server, "first") second_module_res <- callModule(second_module_server, "second", first_module_res) callModule(third_module_server, "third", second_module_res) } # Run the application shinyApp(ui = ui, server = server) 并分批更新10,000吗? EFExtensions BulkUpdate是否具有任何本机功能来支持此功能?

示例:

for loop

资源:

https://entityframework-extensions.net/bulk-update

2 个答案:

答案 0 :(得分:2)

这实际上不是EF的目的。 EF的数据库交互从记录对象开始,然后从那里开始。如果未对实体进行更改跟踪(因此未加载),则EF无法生成部分UPDATE(即不覆盖所有内容),并且类似地,它不能基于条件而不是键来删除记录。

对于条件更新/删除逻辑,例如

,没有等效的EF(不加载所有这些记录)
UPDATE People
SET FirstName = 'Bob'
WHERE FirstName = 'Robert'

DELETE FROM People
WHERE FirstName = 'Robert'

使用EF方法执行此操作将要求您加载所有这些实体,只是将它们发送回(更新或删除)到数据库中,这已经浪费了带宽和性能。< / p>

我在这里找到的最佳解决方案是绕过EF的LINQ友好方法,而是自己执行原始SQL。仍然可以使用EF上下文来完成此操作。

using (var ctx = new MyContext())
{
    string updateCommand = "UPDATE People SET FirstName = 'Bob' WHERE FirstName = 'Robert'";
    int noOfRowsUpdated = ctx.Database.ExecuteSqlCommand(updateCommand);

    string deleteCommand = "DELETE FROM People WHERE FirstName = 'Robert'";
    int noOfRowsDeleted = ctx.Database.ExecuteSqlCommand(deleteCommand);
}

更多信息here。当然,别忘了在相关情况下防止SQL注入

运行原始SQL的特定语法可能因EF / EF Core版本不同而异,但据我所知,所有版本都允许您执行原始SQL。


我无法对EF Extensions或BulkUpdate的性能发表任何评论,也不会从它们那里购买。

根据他们的文档,他们似乎没有带有正确签名的方法来允许条件更新/删除逻辑。

  • BulkUpdate似乎不允许您输入可以优化此条件的逻辑条件(UPDATE命令中的WHERE)。
  • BulkDelete仍然具有BatchSize设置,这表明它们仍一次处理一个记录(我想每批次),并且不使用带有条件的单个DELETE查询(WHERE子句)。

根据问题中的预期代码,EF Extensions并没有真正为您提供所需的内容。在数据库上直接执行原始SQL会更高效,更便宜,因为这避免了EF加载其实体的需要。

更新
我可能会更正,如条件here所示,有一些 支持条件更新逻辑。但是,我不清楚该示例是否仍将所有内容加载到内存中,并且如果您已经将所有内容都加载到内存中,那么该条件WHERE逻辑的目的是什么(为什么不使用内存中的LINQ?)

但是,即使这种方法在不加载实体的情况下仍然有效:

  • 受更大限制(与允许任何有效布尔值的SQL相比,SQL只允许进行相等性检查),
  • 相对复杂(我不喜欢他们的语法,也许那是主观的)
  • 价格更高(仍然是付费图书馆)

与滚动您自己的原始SQL查询相比。我仍然建议在这里滚动自己的原始SQL,但这只是我的意见。

答案 1 :(得分:0)

我发现了使用类似查询条件的批量更新的“适当” EF扩展方式:

var productUpdate = _dbContext.Set<Product>()
    .Where(x => x.ProductType == 'Electronics')
    .UpdateFromQuery( x => new Product { ProductBrand = "ABC Company" });

这将导致生成正确的SQL UPDATE ... SET ... WHERE,而无需按照the documentation首先加载实体:

为什么UpdateFromQuerySaveChangesBulkSaveChangesBulkUpdate快?

UpdateFromQuery直接在SQL中执行一条语句,例如UPDATE [TableName] SET [SetColumnsAndValues] WHERE [Key]

其他操作通常需要一次或多次数据库往返,这会使性能降低。

您可以在BulkUpdate的示例中改编此dotnet fiddle example上的工作语法。

其他注意事项

  • 不幸的是,没有提及此操作的批处理操作。

  • 在进行像这样的大更新之前,可能值得考虑停用此列上可能具有的索引,然后再重建它们。如果您有很多的话,这尤其有用。

  • 请注意Where中的条件,如果EF无法将其转换为SQL,则将在客户端执行,这意味着“通常”可怕的往返行程“加载-更改内存-更新”