diff --git a/.github/actions/dotnet-build/action.yml b/.github/actions/dotnet-build/action.yml
new file mode 100644
index 0000000..3442010
--- /dev/null
+++ b/.github/actions/dotnet-build/action.yml
@@ -0,0 +1,37 @@
+name: dotnet-build
+description: Composite .NET build action
+
+inputs:
+  dotnet-version:
+    default: '10.0.x'
+    required: false
+    description: '.NET version'
+
+  solution:
+    default: 'src/MockQueryable/*.sln'
+    required: false
+    description: 'Solution path'
+
+  configuration:
+    default: 'Release'
+    required: false
+    description: 'Build configuration'
+
+runs:
+  using: composite
+  steps:
+    - name: Setup .NET
+      uses: actions/setup-dotnet@v5
+      with:
+        dotnet-version: ${{ inputs.dotnet-version }}
+
+    - name: Restore
+      shell: bash
+      run: dotnet restore ${{ inputs.solution }}
+
+    - name: Build
+      shell: bash
+      run: |
+        dotnet build ${{ inputs.solution }} \
+          --configuration ${{ inputs.configuration }} \
+          --no-restore
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 1cabed8..b5535b9 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -13,3 +13,9 @@ updates:
       - "/src/MockQueryable/MockQueryable.Sample"
     schedule:
       interval: "weekly"
+  - package-ecosystem: "github-actions"
+    directory: "/"
+    schedule:
+      interval: "weekly"
+    open-pull-requests-limit: 5
+
diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/build.yml
similarity index 70%
rename from .github/workflows/dotnetcore.yml
rename to .github/workflows/build.yml
index e905579..e3d753e 100644
--- a/.github/workflows/dotnetcore.yml
+++ b/.github/workflows/build.yml
@@ -1,4 +1,4 @@
-name: .NET Core
+name: build
 
 on:
   push:
@@ -6,6 +6,11 @@ on:
 permissions:
   contents: read
 
+env:
+  DOTNET_VERSION: 10.0.x
+  SOLUTION: src/MockQueryable/*.sln
+  CONFIGURATION: Release
+
 jobs:
   build:
     runs-on: ubuntu-latest
@@ -13,23 +18,19 @@ jobs:
     steps:
       - uses: actions/checkout@v6
 
-      - name: Setup .NET
-        uses: actions/setup-dotnet@v5
-        with:
-          dotnet-version: '10.0.x'
-
-      - name: Restore
-        run: dotnet restore src/MockQueryable/*.sln
-
       - name: Build
-        run: dotnet build src/MockQueryable/*.sln --configuration Release --no-restore
+        uses: ./.github/actions/dotnet-build
+        with:
+          dotnet-version: ${{ env.DOTNET_VERSION }}
+          solution: ${{ env.SOLUTION }}
+          configuration: ${{ env.CONFIGURATION }}
 
       - name: Test with coverage
         run: |
           mkdir -p TestResults
 
-          dotnet test src/MockQueryable/*.sln \
-            --configuration Release \
+          dotnet test ${{ env.SOLUTION }} \
+            --configuration ${{ env.CONFIGURATION }} \
             --no-build \
             /p:CollectCoverage=true \
             /p:CoverletOutput="$(pwd)/TestResults/coverage.opencover.xml" \
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b3cf034..151ab94 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,6 +11,11 @@ permissions:
   attestations: write
   artifact-metadata: write
 
+env:
+  DOTNET_VERSION: 10.0.x
+  SOLUTION: src/MockQueryable/*.sln
+  CONFIGURATION: Release
+
 jobs:
   release:
     runs-on: ubuntu-latest
@@ -18,22 +23,18 @@ jobs:
     steps:
       - uses: actions/checkout@v6
 
-      - name: Setup .NET
-        uses: actions/setup-dotnet@v5
-        with:
-          dotnet-version: '10.0.x'
-
-      - name: Restore
-        run: dotnet restore src/MockQueryable/*.sln
-
       - name: Build
-        run: dotnet build src/MockQueryable/*.sln --configuration Release --no-restore
+        uses: ./.github/actions/dotnet-build
+        with:
+          dotnet-version: ${{ env.DOTNET_VERSION }}
+          solution: ${{ env.SOLUTION }}
+          configuration: ${{ env.CONFIGURATION }}
 
       - name: Pack
         run: |
           mkdir -p artifacts
-          dotnet pack src/MockQueryable/*.sln \
-            --configuration Release \
+          dotnet pack ${{ env.SOLUTION }} \
+            --configuration ${{ env.CONFIGURATION }} \
             --no-build \
             --output ./artifacts
 
@@ -47,3 +48,5 @@ jobs:
         with:
           files: ./artifacts/*.nupkg
           generate_release_notes: true
+          # prerelease if a tag contains "-"
+          prerelease: ${{ contains(github.ref_name, '-') }}
diff --git a/README.md b/README.md
index 8161709..abe3971 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,8 @@ Extensions for mocking [Entity Framework Core](https://github.com/dotnet/efcore)
 ## ✅ Build & Status
 
 [![codecov](https://codecov.io/github/romantitov/MockQueryable/graph/badge.svg?token=dtiYMUNHUo)](https://codecov.io/github/romantitov/MockQueryable)
-[![.NET Core](https://github.com/romantitov/MockQueryable/workflows/.NET%20Core/badge.svg)](https://github.com/ramantsitou/MockQueryable/actions/workflows/dotnetcore.yml)
+[![build](https://github.com/romantitov/MockQueryable/workflows/build/badge.svg)](https://github.com/ramantsitou/MockQueryable/actions/workflows/build.yml)
+[![release](https://github.com/romantitov/MockQueryable/workflows/release/badge.svg)](https://github.com/ramantsitou/MockQueryable/actions/workflows/release.yml)
 [![License](https://img.shields.io/github/license/romantitov/MockQueryable.svg)](https://github.com/romantitov/MockQueryable/blob/master/LICENSE)
 
 ---
diff --git a/src/MockQueryable/MockQueryable.sln b/src/MockQueryable/MockQueryable.sln
index 35c4ff4..03834c9 100644
--- a/src/MockQueryable/MockQueryable.sln
+++ b/src/MockQueryable/MockQueryable.sln
@@ -1,7 +1,7 @@
 ﻿
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.13.35931.197 d17.13
+# Visual Studio Version 18
+VisualStudioVersion = 18.6.11806.211 stable
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MockQueryable.Core", "MockQueryable.Core\MockQueryable.Core.csproj", "{7C85327C-5EE5-4C74-A1E5-9B7046CA6CFE}"
 EndProject
@@ -17,9 +17,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MockQueryable.EntityFramewo
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
 	ProjectSection(SolutionItems) = preProject
-		..\..\.github\workflows\dotnetcore.yml = ..\..\.github\workflows\dotnetcore.yml
+		..\..\.github\actions\dotnet-build\action.yml = ..\..\.github\actions\dotnet-build\action.yml
+		..\..\.github\workflows\build.yml = ..\..\.github\workflows\build.yml
+		..\..\.github\dependabot.yml = ..\..\.github\dependabot.yml
 		..\..\LICENSE = ..\..\LICENSE
 		..\..\README.md = ..\..\README.md
+		..\..\.github\workflows\release.yml = ..\..\.github\workflows\release.yml
 	EndProjectSection
 EndProject
 Global
