From 8be31d3b41fb8b5e4e6405fc785b21a3f2f7286c Mon Sep 17 00:00:00 2001 From: NorthernLight1 <49600465+NorthernLight1@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:10:52 -0500 Subject: [PATCH 1/3] BulkSaveChangesAsync does not map output PrimaryKeys for inserted entities --- .github/workflows/release.yml | 80 +++++++++++++++++++ .../DbContextExtensions/BulkInsert.cs | 7 +- .../DbContextExtensions/BulkSaveChanges.cs | 1 + .../BulkSaveChangesAsync.cs | 1 + ...EntityFrameworkCore.Extensions.Test.csproj | 6 +- .../Data/DbContextExtensions.cs | 9 ++- 6 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d7f635b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,80 @@ +# Manual workflow: publish to NuGet and create a GitHub release with source zip. +# Run from Actions tab → "Publish and Release" → "Run workflow". +# Required secrets: NUGET_API_KEY (https://www.nuget.org/account/apikeys) + +name: Publish and Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g. 8.0.0.x). Tag will be v{version}.' + required: true + type: string + nuget_publish: + description: 'Publish package to NuGet.org' + required: false + default: true + type: boolean + +env: + NUGET_SOURCE: 'https://api.nuget.org/v3/index.json' + +jobs: + publish-and-release: + runs-on: windows-latest + permissions: + contents: write # create tag and release, upload assets + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '10.0.x' + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore -c Release + + - name: Pack (ensure .nupkg output) + run: dotnet pack N.EntityFrameworkCore.Extensions/N.EntityFrameworkCore.Extensions.csproj --no-build -c Release -o out + id: pack + + - name: Publish to NuGet + if: ${{ inputs.nuget_publish }} + run: dotnet nuget push .\out\*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source ${{ env.NUGET_SOURCE }} --skip-duplicate + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + + - name: Create source zip + run: | + $tag = "v${{ inputs.version }}" + git archive --format=zip --output=source-$tag.zip HEAD + echo "SOURCE_ZIP=source-$tag.zip" >> $env:GITHUB_ENV + echo "ARCHIVE_NAME=source-$tag.zip" >> $env:GITHUB_ENV + + - name: Get .nupkg path + id: nupkg + run: | + $nupkg = Get-ChildItem -Path out -Filter "*.nupkg" | Select-Object -First 1 + echo "path=$($nupkg.FullName)" >> $env:GITHUB_OUTPUT + echo "name=$($nupkg.Name)" >> $env:GITHUB_OUTPUT + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ inputs.version }} + name: Release v${{ inputs.version }} + generate_release_notes: true + files: | + ${{ env.ARCHIVE_NAME }} + ${{ steps.nupkg.outputs.path }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkInsert.cs b/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkInsert.cs index ae7e5f0..81b2b01 100644 --- a/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkInsert.cs +++ b/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkInsert.cs @@ -378,12 +378,15 @@ public void With_Trigger() { products.Add(new ProductWithTrigger { Id = i.ToString(), Price = 1.57M, StatusString="InStock" }); } - int rowsInserted = dbContext.BulkInsert(products, options => { + + //The return int from BulkInsert() will be off when using triggers + dbContext.BulkInsert(products, options => { options.AutoMapOutput = false; options.BulkCopyOptions = SqlBulkCopyOptions.FireTriggers; }); + var rowsInserted = dbContext.ProductsWithTrigger.Count(); - Assert.IsTrue(rowsInserted == products.Count, "The number of rows inserted must match the count of products"); + Assert.IsTrue(rowsInserted == products.Count , $"The number of rows inserted must match the count of products ({rowsInserted}!={products.Count})"); } [TestMethod] public void With_ValueGenerated_Default() diff --git a/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkSaveChanges.cs b/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkSaveChanges.cs index 19562ea..d2f9fbc 100644 --- a/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkSaveChanges.cs +++ b/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkSaveChanges.cs @@ -61,6 +61,7 @@ public void With_Add_Changes() int rowsAffected = dbContext.BulkSaveChanges(); int newTotalCount = dbContext.Orders.Where(o => o.Price == 10.57M).Count(); + Assert.IsTrue(ordersToAdd.Where(o => o.Id <= 0).Count() == 0, "Primary key should have been updated for all entities"); Assert.IsTrue(rowsAffected == ordersToAdd.Count, "The number of rows affected must equal the sum of entities added, deleted and updated"); Assert.IsTrue(oldTotalCount + ordersToAdd.Count == newTotalCount, "The number of orders to add did not match what was expected."); } diff --git a/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkSaveChangesAsync.cs b/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkSaveChangesAsync.cs index 9bb39b9..ceaea35 100644 --- a/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkSaveChangesAsync.cs +++ b/N.EntityFrameworkCore.Extensions.Test/DbContextExtensions/BulkSaveChangesAsync.cs @@ -63,6 +63,7 @@ public async Task With_Add_Changes() int rowsAffected = await dbContext.BulkSaveChangesAsync(); int newTotalCount = dbContext.Orders.Where(o => o.Price == 10.57M).Count(); + Assert.IsTrue(ordersToAdd.Where(o => o.Id <= 0).Count() == 0, "Primary key should have been updated for all entities"); Assert.IsTrue(rowsAffected == ordersToAdd.Count, "The number of rows affected must equal the sum of entities added, deleted and updated"); Assert.IsTrue(oldTotalCount + ordersToAdd.Count == newTotalCount, "The number of orders to add did not match what was expected."); } diff --git a/N.EntityFrameworkCore.Extensions.Test/N.EntityFrameworkCore.Extensions.Test.csproj b/N.EntityFrameworkCore.Extensions.Test/N.EntityFrameworkCore.Extensions.Test.csproj index 8e1c263..705f519 100644 --- a/N.EntityFrameworkCore.Extensions.Test/N.EntityFrameworkCore.Extensions.Test.csproj +++ b/N.EntityFrameworkCore.Extensions.Test/N.EntityFrameworkCore.Extensions.Test.csproj @@ -17,9 +17,9 @@ - - - + + + diff --git a/N.EntityFrameworkCore.Extensions/Data/DbContextExtensions.cs b/N.EntityFrameworkCore.Extensions/Data/DbContextExtensions.cs index fccd9fb..a68ee1e 100644 --- a/N.EntityFrameworkCore.Extensions/Data/DbContextExtensions.cs +++ b/N.EntityFrameworkCore.Extensions/Data/DbContextExtensions.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Linq.Expressions; +using System.Runtime.CompilerServices; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; @@ -247,8 +248,12 @@ internal static void SetStoreGeneratedValues(this DbContext context, T entity { foreach (var property in properties) { - if (!property.IsPrimaryKey() || - (property.IsPrimaryKey() && updateEntry.EntityState == EntityState.Detached)) + if((updateEntry.EntityState == EntityState.Added && + (property.ValueGenerated == ValueGenerated.OnAdd|| property.ValueGenerated == ValueGenerated.OnAddOrUpdate)) || + (updateEntry.EntityState == EntityState.Modified && + (property.ValueGenerated == ValueGenerated.OnUpdate || property.ValueGenerated == ValueGenerated.OnAddOrUpdate)) || + updateEntry.EntityState == EntityState.Detached + ) { try { From 370e0c298ee8ae88c5b6773cf0da8bbbefc72192 Mon Sep 17 00:00:00 2001 From: NorthernLight1 <49600465+NorthernLight1@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:12:45 -0500 Subject: [PATCH 2/3] Update N.EntityFrameworkCore.Extensions.csproj Nuget version updated to 8.0.0.13 --- .../N.EntityFrameworkCore.Extensions.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/N.EntityFrameworkCore.Extensions/N.EntityFrameworkCore.Extensions.csproj b/N.EntityFrameworkCore.Extensions/N.EntityFrameworkCore.Extensions.csproj index 54607a8..48f1c1f 100644 --- a/N.EntityFrameworkCore.Extensions/N.EntityFrameworkCore.Extensions.csproj +++ b/N.EntityFrameworkCore.Extensions/N.EntityFrameworkCore.Extensions.csproj @@ -2,11 +2,11 @@ net8.0 - 8.0.0.12 + 8.0.0.13 true https://github.com/NorthernLight1/N.EntityFrameworkCore.Extensions/ Northern25 - Copyright © 2024 + Copyright © 2026 N.EntityFrameworkCore.Extensions extends your DbContext in EF Core with high-performance bulk operations: BulkDelete, BulkInsert, BulkMerge, BulkSync, BulkUpdate, Fetch, DeleteFromQuery, InsertFromQuery, UpdateFromQuery. From 00c169c61f97f8fef6a337c585ec7e641a804058 Mon Sep 17 00:00:00 2001 From: NorthernLight1 <49600465+NorthernLight1@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:14:22 -0500 Subject: [PATCH 3/3] Potential fix for code scanning alert no. 1: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/dotnet.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index a927291..f73ac71 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -6,6 +6,9 @@ on: pull_request: branches: [ "master" ] +permissions: + contents: read + jobs: build: