Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ef core generates a query for each row after AddRange() then SaveChanges() #21544

Closed
amro93 opened this issue Jul 7, 2020 · 5 comments
Closed
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@amro93
Copy link

amro93 commented Jul 7, 2020

workflowStateRepository.CreateMany(wfStates);
await workflowStateRepository.SaveAsync();

public virtual void CreateMany(IEnumerable entities)
{
if (entities is null)
{
throw new ArgumentNullException(nameof(entities));
}
SetCreateEntityDetails(ref entities);
Set.AddRange(entities);
}

result query:

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (78ms) [Parameters=[@p0='string' (Size = 4000), @p1='user.test' (Size = 100), @p2='2020-07-07T10:42:55' (Nullable = true), @p3=NULL (DbType = DateTime2), @p4='1st' (Size = 4000), @p5='41c2c594-0609-49fc-aa01-3f574803f3b1' (Nullable = false) (Size = 100), @p6='False', @p7='1st Step' (Nullable = false) (Size = 100), @p8='Automatic' (Nullable = false) (Size = 50), @p9='Start' (Nullable = false) (Size = 50), @p10=NULL (Size = 100), @p11=NULL (DbType = DateTime2), @p12=NULL (Size = 4000), @p13='Normal' (Nullable = false) (Size = 50), @p14='19'], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [WorkflowState] ([BusinessComments], [CreatedBy], [CreatedDate], [DeletedDate], [Description], [Guid], [IsDeleted], [Name], [PickUserMode], [Status], [UpdatedBy], [UpdatedDate], [WorkflowDiagram], [WorkflowStateType], [WorkflowVersionId])
      VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14);
      SELECT [Id]
      FROM [WorkflowState]
      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (4ms) [Parameters=[@p0='string' (Size = 4000), @p1='user.test' (Size = 100), @p2='2020-07-07T10:42:55' (Nullable = true), @p3=NULL (DbType = DateTime2), @p4='2nd' (Size = 4000), @p5='a29d8d49-84d3-4514-a41a-da78e980714f' (Nullable = false) (Size = 100), @p6='False', @p7='2nd Step' (Nullable = false) (Size = 100), @p8='Automatic' (Nullable = false) (Size = 50), @p9='Intermediate' (Nullable = false) (Size = 50), @p10=NULL (Size = 100), @p11=NULL (DbType = DateTime2), @p12=NULL (Size = 4000), @p13='Normal' (Nullable = false) (Size = 50), @p14='19'], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [WorkflowState] ([BusinessComments], [CreatedBy], [CreatedDate], [DeletedDate], [Description], [Guid], [IsDeleted], [Name], [PickUserMode], [Status], [UpdatedBy], [UpdatedDate], [WorkflowDiagram], [WorkflowStateType], [WorkflowVersionId])
      VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14);
      SELECT [Id]
      FROM [WorkflowState]
      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (7ms) [Parameters=[@p0='string' (Size = 4000), @p1='user.test' (Size = 100), @p2='2020-07-07T10:42:55' (Nullable = true), @p3=NULL (DbType = DateTime2), @p4='3rd' (Size = 4000), @p5='8530b3d5-4d0e-467d-ad1c-bbead04102ff' (Nullable = false) (Size = 100), @p6='False', @p7='3rd Step' (Nullable = false) (Size = 100), @p8='Automatic' (Nullable = false) (Size = 50), @p9='End' (Nullable = false) (Size = 50), @p10=NULL (Size = 100), @p11=NULL (DbType = DateTime2), @p12=NULL (Size = 4000), @p13='Normal' (Nullable = false) (Size = 50), @p14='19'], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [WorkflowState] ([BusinessComments], [CreatedBy], [CreatedDate], [DeletedDate], [Description], [Guid], [IsDeleted], [Name], [PickUserMode], [Status], [UpdatedBy], [UpdatedDate], [WorkflowDiagram], [WorkflowStateType], [WorkflowVersionId])
      VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14);
      SELECT [Id]
      FROM [WorkflowState]
      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();


```### Steps to reproduce

<!--
What steps can we follow to reproduce the issue?

We ❤ code! Include a complete code listing or attach a simplified project

``` C#
public async Task<WorkflowVersionDto> CreateAsync(CreateWorkflowVersionDto dto)
        {
            var entity = workflowVersionDtoMap.DtoToEntity(dto);
            // TODO: entity mapping validation
            // TODO: enhance query below
            var wfStates = entity.WorkflowStates?.ToList();
            entity.WorkflowStates = null;

            await workflowVersionRepository.CreateAsync(entity);
            await workflowVersionRepository.SaveAsync();

            if (wfStates != null)
            {
                List<WorkflowDueActionType> actions = new List<WorkflowDueActionType>();
                List<WorkflowDueTime> dueTimes = new List<WorkflowDueTime>();
                List<WorkflowTransition> workflowTransitions = new List<WorkflowTransition>();
                List<WorkflowStateGroup> workflowStateGroups = new List<WorkflowStateGroup>();
                List<WorkflowStateUser> workflowStateUsers = new List<WorkflowStateUser>();

                // separate nested object to be saved on another object
                foreach (var state in wfStates)
                {
                    if (state.WorkflowDueActionTypes != null)
                    {
                        state.WorkflowDueActionTypes.ToList().ForEach(d => d.WorkflowState = state);
                        actions.AddRange(state.WorkflowDueActionTypes);
                        state.WorkflowDueActionTypes = null;
                    }
                    if (state.WorkflowDueTime != null)
                    {
                        state.WorkflowDueTime.WorkflowState = state;
                        dueTimes.Add(state.WorkflowDueTime);
                        state.WorkflowDueTime = null;
                    }

                    if (state.BeginWorkflowTransitions != null)
                    {
                        state.BeginWorkflowTransitions.ToList().ForEach(t => t.BeginWorkflowState = state);
                        workflowTransitions.AddRange(state.BeginWorkflowTransitions);
                        state.BeginWorkflowTransitions = null;
                    }

                    if (state.WorkflowStateGroups != null)
                    {
                        state.WorkflowStateGroups.ToList().ForEach(g => g.WorkflowState = state);
                        workflowStateGroups.AddRange(state.WorkflowStateGroups);
                        state.WorkflowStateGroups = null;
                    }

                    if (state.WorkflowStateUsers != null)
                    {
                        state.WorkflowStateUsers.ToList().ForEach(u => u.WorkflowState = state);
                        workflowStateUsers.AddRange(state.WorkflowStateUsers);
                        state.WorkflowStateUsers = null;
                    }
                }
               
               // Here =>>> expecting 100s of WfState objects
                workflowStateRepository.CreateMany(wfStates);
                await workflowStateRepository.SaveAsync(); // save each row in a single query

                // then
                workflowDueActionTypeRepository.CreateMany(actions); // save all rows in a single query

                workflowDueTimeRepository.CreateMany(dueTimes); // save all rows in a single query

                workflowTransitionRepository.CreateMany(workflowTransitions); // save all rows in a single query

                workflowStateGroupRepository.CreateMany(workflowStateGroups); // save all rows in a single query

                workflowStateUserRepository.CreateMany(workflowStateUsers); // save all rows in a single query

                await workflowStateRepository.SaveAsync(); // save all rows in a single query
            }
            return await GetWithStatesAsync(entity.Id);
        }

Got Exceptions? Include both the message and the stack trace

For dotnet ef and PMC, share the --verbose output
-->

Further technical details

EF Core version: 3.1.5
Database provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer)
Target framework: (e.g. .NET Core 3.0)
Operating system:
IDE: (e.g. Visual Studio 2019 16.3)

@roji
Copy link
Member

roji commented Jul 7, 2020

@amro93 although a separate INSERT is sent for each added row, these INSERTs are batched in a single round-trip, providing good performance. It's also necessary to have separate INSERTs in order to get any database-generated values (e.g. identity) back from the database, and populate them in the inserted entities. Do you have a particular problem or alternative implementation in mind here?

@amro93
Copy link
Author

amro93 commented Jul 7, 2020

@amro93 although a separate INSERT is sent for each added row, these INSERTs are batched in a single round-trip, providing good performance. It's also necessary to have separate INSERTs in order to get any database-generated values (e.g. identity) back from the database, and populate them in the inserted entities. Do you have a particular problem or alternative implementation in mind here?

@roji thank you for the fast response,

its OK if the query is sent on a single round trip with multiple inserts, but I'd expect the logger to log this row "info: Microsoft.EntityFrameworkCore.Database.Command[20101]" once, which indicates that the transaction has been started.
but if you have a deep look on the included query you will notice that the transaction initialized multiple times for each single row, which I believe its not a single round trip

however the next lines of the code will be translated into the query below which make sense for a single round trip

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (46ms) [Parameters=[@p0='SendMailDueActionType' (Nullable = false) (Size = 100), @p1='54', @p2='this is over due test mail body' (Nullable = false) (Size = 4000), @p3='False', @P4='AssignedUser' (Nullable = false) (Size = 100), @p5='over due Task ' (Nullable = false) (Size = 4000), @p6='MoveToStateDueActionType' (Nullable = false) (Size = 100), @P7='54', @p8='41c2c594-0609-49fc-aa01-3f574803f3b1' (Size = 4000), @p9='53', @p10='54', @p11='Hours' (Nullable = false) (Size = 20), @p12='2', @P13='user.test' (Size = 100), @p14='2020-07-07T11:08:21' (Nullable = true), @P15='1', @p16='53', @p17='user.test' (Size = 100), @p18='2020-07-07T11:08:21' (Nullable = true), @p19='2', @p20='54', @p21='53', @p22='user.test' (Size = 100), @p23='2020-07-07T11:08:20' (Nullable = true), @p24='54', @p25='54', @P26='user.test' (Size = 100), @p27='2020-07-07T11:08:20' (Nullable = true), @p28='55'], CommandType='Text', CommandTimeout='30']

SET NOCOUNT ON;
INSERT INTO [WorkflowDueActionType] ([Discriminator], [WorkflowStateId], [Body], [IsBodyHtml], [MailReceiverType], [Subject])
VALUES (@p0, @p1, @p2, @p3, @P4, @p5);
SELECT [Id]
FROM [WorkflowDueActionType]
WHERE @@rowcount = 1 AND [Id] = scope_identity();

  INSERT INTO [WorkflowDueActionType] ([Discriminator], [WorkflowStateId], [NextWorkflowStateGuid], [NextWorkflowStateId])
  VALUES (@p6, @p7, @p8, @p9);
  SELECT [Id]
  FROM [WorkflowDueActionType]
  WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
  
  INSERT INTO [WorkflowDueTime] ([WorkflowStateId], [DueTimeType], [DueTimeValue])
  VALUES (@p10, @p11, @p12);
  DECLARE @inserted3 TABLE ([Id] bigint, [_Position] [int]);
  MERGE [WorkflowStateGroup] USING (
  VALUES (@p13, @p14, @p15, @p16, 0),
  (@p17, @p18, @p19, @p20, 1)) AS i ([CreatedBy], [CreatedDate], [GroupId], [WorkflowStateId], _Position) ON 1=0
  WHEN NOT MATCHED THEN
  INSERT ([CreatedBy], [CreatedDate], [GroupId], [WorkflowStateId])
  VALUES (i.[CreatedBy], i.[CreatedDate], i.[GroupId], i.[WorkflowStateId])
  OUTPUT INSERTED.[Id], i._Position
  INTO @inserted3;
  
  SELECT [t].[Id] FROM [WorkflowStateGroup] t
  INNER JOIN @inserted3 i ON ([t].[Id] = [i].[Id])
  ORDER BY [i].[_Position];
  
  DECLARE @inserted5 TABLE ([Id] bigint, [_Position] [int]);
  MERGE [WorkflowTransition] USING (
  VALUES (@p21, @p22, @p23, @p24, 0),
  (@p25, @p26, @p27, @p28, 1)) AS i ([BeginWorkflowStateId], [CreatedBy], [CreatedDate], [EndWorkflowStateId], _Position) ON 1=0
  WHEN NOT MATCHED THEN
  INSERT ([BeginWorkflowStateId], [CreatedBy], [CreatedDate], [EndWorkflowStateId])
  VALUES (i.[BeginWorkflowStateId], i.[CreatedBy], i.[CreatedDate], i.[EndWorkflowStateId])
  OUTPUT INSERTED.[Id], i._Position
  INTO @inserted5;
  
  SELECT [t].[Id] FROM [WorkflowTransition] t
  INNER JOIN @inserted5 i ON ([t].[Id] = [i].[Id])
  ORDER BY [i].[_Position];

@amro93
Copy link
Author

amro93 commented Jul 7, 2020

I wrote a unit test which simulates the same exact code and it generated the expected query successfully

unit test

[TestClass]
    public class WorkflowVersionServiceTest
    {

        readonly IServiceProvider serviceProvider;
        readonly IConfiguration configuration;

        public WorkflowVersionServiceTest()
        {
            var myConfiguration = new Dictionary<string, string>
            {
                {"ConnectionStrings:DefaultConnection", "Server=DESKTOP-3DR4F68;Database=WorkflowDev;user id=sa;password=P@$$w0rd;MultipleActiveResultSets=true;Initial Catalog=WorkflowDev1"}
            };
            var config = new ConfigurationBuilder().AddInMemoryCollection(myConfiguration).Build();
            configuration = config;
            serviceProvider = IoC.IoCServices.AddAppServices(new ServiceCollection(), config, LoggerFactory.Create(a =>
            {
                a.AddDebug();
                a.AddConsole();
            })).AddLogging(o => {
                o.AddConsole(c => c.DisableColors = false);
                o.AddDebug();
            }).BuildServiceProvider();
        }

        [TestMethod]
        public async Task CreateMany()
        {
            var ctx = serviceProvider.GetRequiredService<ApplicationDbContext>();

            var repo = new WorkflowStateRepository(
                ctx,
                serviceProvider
                );
            var wfStates = new List<WorkflowState>();
            for (int i = 0; i <= 10; i++)
            {
                wfStates.Add(
                new WorkflowState
                {
                    Guid = Guid.NewGuid().ToString(),
                    WorkflowVersionId = 10,
                    Name = $"state {i}",
                });
            }

            repo.CreateMany(wfStates);
            await repo.SaveAsync();
        }
    }

Result query

Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (92ms) [Parameters=[@p0=NULL (Size = 4000), @p1=NULL (Size = 100), @p2='2020-07-07T18:27:38' (Nullable = true), @p3=NULL (DbType = DateTime2), @p4=NULL (Size = 4000), @p5='c45d87b0-792b-4eef-95bb-ac2efe2bd549' (Nullable = false) (Size = 100), @p6='False', @p7='state 0' (Nullable = false) (Size = 100), @p8='Manual' (Nullable = false) (Size = 50), @p9='Start' (Nullable = false) (Size = 50), @p10=NULL (Size = 100), @p11=NULL (DbType = DateTime2), @p12=NULL (Size = 4000), @p13='Normal' (Nullable = false) (Size = 50), @p14='10', @p15=NULL (Size = 4000), @p16=NULL (Size = 100), @p17='2020-07-07T18:27:38' (Nullable = true), @p18=NULL (DbType = DateTime2), @p19=NULL (Size = 4000), @p20='28a1faa1-c170-453f-b56d-c1ebb51b8584' (Nullable = false) (Size = 100), @p21='False', @p22='state 1' (Nullable = false) (Size = 100), @p23='Manual' (Nullable = false) (Size = 50), @p24='Start' (Nullable = false) (Size = 50), @p25=NULL (Size = 100), @p26=NULL (DbType = DateTime2), @p27=NULL (Size = 4000), @p28='Normal' (Nullable = false) (Size = 50), @p29='10', @p30=NULL (Size = 4000), @p31=NULL (Size = 100), @p32='2020-07-07T18:27:38' (Nullable = true), @p33=NULL (DbType = DateTime2), @p34=NULL (Size = 4000), @p35='14c8fbcb-09b6-4d31-bc61-24b9e992b047' (Nullable = false) (Size = 100), @p36='False', @p37='state 2' (Nullable = false) (Size = 100), @p38='Manual' (Nullable = false) (Size = 50), @p39='Start' (Nullable = false) (Size = 50), @p40=NULL (Size = 100), @p41=NULL (DbType = DateTime2), @p42=NULL (Size = 4000), @p43='Normal' (Nullable = false) (Size = 50), @p44='10', @p45=NULL (Size = 4000), @p46=NULL (Size = 100), @p47='2020-07-07T18:27:38' (Nullable = true), @p48=NULL (DbType = DateTime2), @p49=NULL (Size = 4000), @p50='c414c398-54e0-4627-8630-6c86b64e0055' (Nullable = false) (Size = 100), @p51='False', @p52='state 3' (Nullable = false) (Size = 100), @p53='Manual' (Nullable = false) (Size = 50), @p54='Start' (Nullable = false) (Size = 50), @p55=NULL (Size = 100), @p56=NULL (DbType = DateTime2), @p57=NULL (Size = 4000), @p58='Normal' (Nullable = false) (Size = 50), @p59='10', @p60=NULL (Size = 4000), @p61=NULL (Size = 100), @p62='2020-07-07T18:27:38' (Nullable = true), @p63=NULL (DbType = DateTime2), @p64=NULL (Size = 4000), @p65='8fa009aa-e375-4a7b-8117-8cbd3de36083' (Nullable = false) (Size = 100), @p66='False', @p67='state 4' (Nullable = false) (Size = 100), @p68='Manual' (Nullable = false) (Size = 50), @p69='Start' (Nullable = false) (Size = 50), @p70=NULL (Size = 100), @p71=NULL (DbType = DateTime2), @p72=NULL (Size = 4000), @p73='Normal' (Nullable = false) (Size = 50), @p74='10', @p75=NULL (Size = 4000), @p76=NULL (Size = 100), @p77='2020-07-07T18:27:38' (Nullable = true), @p78=NULL (DbType = DateTime2), @p79=NULL (Size = 4000), @p80='80caa179-f575-49f6-bf18-d6c68017132b' (Nullable = false) (Size = 100), @p81='False', @p82='state 5' (Nullable = false) (Size = 100), @p83='Manual' (Nullable = false) (Size = 50), @p84='Start' (Nullable = false) (Size = 50), @p85=NULL (Size = 100), @p86=NULL (DbType = DateTime2), @p87=NULL (Size = 4000), @p88='Normal' (Nullable = false) (Size = 50), @p89='10', @p90=NULL (Size = 4000), @p91=NULL (Size = 100), @p92='2020-07-07T18:27:38' (Nullable = true), @p93=NULL (DbType = DateTime2), @p94=NULL (Size = 4000), @p95='5c4933e2-d79c-476c-b799-65ff89fb15f6' (Nullable = false) (Size = 100), @p96='False', @p97='state 6' (Nullable = false) (Size = 100), @p98='Manual' (Nullable = false) (Size = 50), @p99='Start' (Nullable = false) (Size = 50), @p100=NULL (Size = 100), @p101=NULL (DbType = DateTime2), @p102=NULL (Size = 4000), @p103='Normal' (Nullable = false) (Size = 50), @p104='10', @p105=NULL (Size = 4000), @p106=NULL (Size = 100), @p107='2020-07-07T18:27:38' (Nullable = true), @p108=NULL (DbType = DateTime2), @p109=NULL (Size = 4000), @p110='555c1625-8fcf-4890-b5ae-de6157bfd7c9' (Nullable = false) (Size = 100), @p111='False', @p112='state 7' (Nullable = false) (Size = 100), @p113='Manual' (Nullable = false) (Size = 50), @p114='Start' (Nullable = false) (Size = 50), @p115=NULL (Size = 100), @p116=NULL (DbType = DateTime2), @p117=NULL (Size = 4000), @p118='Normal' (Nullable = false) (Size = 50), @p119='10', @p120=NULL (Size = 4000), @p121=NULL (Size = 100), @p122='2020-07-07T18:27:38' (Nullable = true), @p123=NULL (DbType = DateTime2), @p124=NULL (Size = 4000), @p125='4c07b2f0-a91a-4b1e-a4cc-47275f0a7caa' (Nullable = false) (Size = 100), @p126='False', @p127='state 8' (Nullable = false) (Size = 100), @p128='Manual' (Nullable = false) (Size = 50), @p129='Start' (Nullable = false) (Size = 50), @p130=NULL (Size = 100), @p131=NULL (DbType = DateTime2), @p132=NULL (Size = 4000), @p133='Normal' (Nullable = false) (Size = 50), @p134='10', @p135=NULL (Size = 4000), @p136=NULL (Size = 100), @p137='2020-07-07T18:27:38' (Nullable = true), @p138=NULL (DbType = DateTime2), @p139=NULL (Size = 4000), @p140='69d42d91-3d95-4944-b78f-729ccf3a0c3c' (Nullable = false) (Size = 100), @p141='False', @p142='state 9' (Nullable = false) (Size = 100), @p143='Manual' (Nullable = false) (Size = 50), @p144='Start' (Nullable = false) (Size = 50), @p145=NULL (Size = 100), @p146=NULL (DbType = DateTime2), @p147=NULL (Size = 4000), @p148='Normal' (Nullable = false) (Size = 50), @p149='10', @p150=NULL (Size = 4000), @p151=NULL (Size = 100), @p152='2020-07-07T18:27:38' (Nullable = true), @p153=NULL (DbType = DateTime2), @p154=NULL (Size = 4000), @p155='fd4fdb28-c638-49b9-92b7-baaa90e09f7c' (Nullable = false) (Size = 100), @p156='False', @p157='state 10' (Nullable = false) (Size = 100), @p158='Manual' (Nullable = false) (Size = 50), @p159='Start' (Nullable = false) (Size = 50), @p160=NULL (Size = 100), @p161=NULL (DbType = DateTime2), @p162=NULL (Size = 4000), @p163='Normal' (Nullable = false) (Size = 50), @p164='10'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DECLARE @inserted0 TABLE ([Id] bigint, [_Position] [int]);
MERGE [WorkflowState] USING (
VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, 0),
(@p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26, @p27, @p28, @p29, 1),
(@p30, @p31, @p32, @p33, @p34, @p35, @p36, @p37, @p38, @p39, @p40, @p41, @p42, @p43, @p44, 2),
(@p45, @p46, @p47, @p48, @p49, @p50, @p51, @p52, @p53, @p54, @p55, @p56, @p57, @p58, @p59, 3),
(@p60, @p61, @p62, @p63, @p64, @p65, @p66, @p67, @p68, @p69, @p70, @p71, @p72, @p73, @p74, 4),
(@p75, @p76, @p77, @p78, @p79, @p80, @p81, @p82, @p83, @p84, @p85, @p86, @p87, @p88, @p89, 5),
(@p90, @p91, @p92, @p93, @p94, @p95, @p96, @p97, @p98, @p99, @p100, @p101, @p102, @p103, @p104, 6),
(@p105, @p106, @p107, @p108, @p109, @p110, @p111, @p112, @p113, @p114, @p115, @p116, @p117, @p118, @p119, 7),
(@p120, @p121, @p122, @p123, @p124, @p125, @p126, @p127, @p128, @p129, @p130, @p131, @p132, @p133, @p134, 8),
(@p135, @p136, @p137, @p138, @p139, @p140, @p141, @p142, @p143, @p144, @p145, @p146, @p147, @p148, @p149, 9),
(@p150, @p151, @p152, @p153, @p154, @p155, @p156, @p157, @p158, @p159, @p160, @p161, @p162, @p163, @p164, 10)) AS i ([BusinessComments], [CreatedBy], [CreatedDate], [DeletedDate], [Description], [Guid], [IsDeleted], [Name], [PickUserMode], [Status], [UpdatedBy], [UpdatedDate], [WorkflowDiagram], [WorkflowStateType], [WorkflowVersionId], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([BusinessComments], [CreatedBy], [CreatedDate], [DeletedDate], [Description], [Guid], [IsDeleted], [Name], [PickUserMode], [Status], [UpdatedBy], [UpdatedDate], [WorkflowDiagram], [WorkflowStateType], [WorkflowVersionId])
VALUES (i.[BusinessComments], i.[CreatedBy], i.[CreatedDate], i.[DeletedDate], i.[Description], i.[Guid], i.[IsDeleted], i.[Name], i.[PickUserMode], i.[Status], i.[UpdatedBy], i.[UpdatedDate], i.[WorkflowDiagram], i.[WorkflowStateType], i.[WorkflowVersionId])
OUTPUT INSERTED.[Id], i._Position
INTO @inserted0;

SELECT [t].[Id] FROM [WorkflowState] t
INNER JOIN @inserted0 i ON ([t].[Id] = [i].[Id])
ORDER BY [i].[_Position];
The thread 0x37bc has exited with code 0 (0x0).
The thread 0x5e54 has exited with code 0 (0x0).
The program '[23056] testhost.x86.exe: Program Trace' has exited with code 0 (0x0).
The program '[23056] testhost.x86.exe' has exited with code 0 (0x0).

@amro93
Copy link
Author

amro93 commented Jul 8, 2020

When I added large number of rows 1000+ ef core generated a huge query for a single round trip.
it seems that ef core will decide when to generate a single query for each row if the number of rows is less than a specific value which intended to improve the performance after all.

@amro93 amro93 closed this as completed Jul 8, 2020
@roji
Copy link
Member

roji commented Jul 8, 2020

@amro93 yeah, the SQL Server provider has a minimum below which it doesn't batch, because that has been shown to be less efficient - see #9270 for some more info on this. FWIW the PostgreSQL providers starting from two commands, so this is a provider-specific thing.

@ajcvickers ajcvickers added the closed-no-further-action The issue is closed and no further action is planned. label Jul 9, 2020
@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

3 participants