From 3c4ebb506e2d953d14fc8d2ea43b56189349ba19 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:27:06 +0000
Subject: [PATCH 1/4] Initial plan
From 9b97f5086b248c2244a4e03a7d0c8e837469b564 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:29:08 +0000
Subject: [PATCH 2/4] Add Riok.Mapperly package
Agent-Logs-Url: https://github.com/gothinkster/aspnetcore-realworld-example-app/sessions/1ad5903e-1320-47fb-889d-8728ec9c60ee
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
---
Directory.Packages.props | 3 ++-
src/Conduit/Conduit.csproj | 1 +
src/Conduit/packages.lock.json | 6 ++++++
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 40f95ec..d5b8542 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,6 +9,7 @@
+
@@ -19,4 +20,4 @@
-
+
\ No newline at end of file
diff --git a/src/Conduit/Conduit.csproj b/src/Conduit/Conduit.csproj
index fe9f26f..1379ffc 100644
--- a/src/Conduit/Conduit.csproj
+++ b/src/Conduit/Conduit.csproj
@@ -6,6 +6,7 @@
+
diff --git a/src/Conduit/packages.lock.json b/src/Conduit/packages.lock.json
index 5674710..236797c 100644
--- a/src/Conduit/packages.lock.json
+++ b/src/Conduit/packages.lock.json
@@ -72,6 +72,12 @@
"Microsoft.EntityFrameworkCore.Relational": "10.0.2"
}
},
+ "Riok.Mapperly": {
+ "type": "Direct",
+ "requested": "[4.3.1, )",
+ "resolved": "4.3.1",
+ "contentHash": "oSE+OVLHyiojrws0QVbQaAT2YKsYbyHqJXPdV7EhGseR/dYgeWS2x1T5qY5I4E+RJkJqMSG+FyZOq4SljWgJWQ=="
+ },
"Serilog": {
"type": "Direct",
"requested": "[4.3.0, )",
From 6aa1cb30bd03e31bdbdb88701889d3501e5d4254 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:35:36 +0000
Subject: [PATCH 3/4] Replace AutoMapper with Mapperly
Agent-Logs-Url: https://github.com/gothinkster/aspnetcore-realworld-example-app/sessions/1ad5903e-1320-47fb-889d-8728ec9c60ee
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
---
Directory.Packages.props | 1 -
src/Conduit/Conduit.csproj | 3 +-
src/Conduit/Features/ConduitMapper.cs | 29 +++++++++++++++++++
.../Features/Profiles/MappingProfile.cs | 8 -----
.../Features/Profiles/ProfileReader.cs | 5 ++--
src/Conduit/Features/Users/Create.cs | 5 ++--
src/Conduit/Features/Users/Details.cs | 5 ++--
src/Conduit/Features/Users/Edit.cs | 5 ++--
src/Conduit/Features/Users/Login.cs | 5 ++--
src/Conduit/Features/Users/MappingProfile.cs | 8 -----
src/Conduit/ServicesExtensions.cs | 2 +-
src/Conduit/packages.lock.json | 6 ----
.../packages.lock.json | 17 +++++------
13 files changed, 48 insertions(+), 51 deletions(-)
create mode 100644 src/Conduit/Features/ConduitMapper.cs
delete mode 100644 src/Conduit/Features/Profiles/MappingProfile.cs
delete mode 100644 src/Conduit/Features/Users/MappingProfile.cs
diff --git a/Directory.Packages.props b/Directory.Packages.props
index d5b8542..493d673 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,6 +1,5 @@
-
diff --git a/src/Conduit/Conduit.csproj b/src/Conduit/Conduit.csproj
index 1379ffc..aa1b5a0 100644
--- a/src/Conduit/Conduit.csproj
+++ b/src/Conduit/Conduit.csproj
@@ -1,12 +1,11 @@
-
+
-
diff --git a/src/Conduit/Features/ConduitMapper.cs b/src/Conduit/Features/ConduitMapper.cs
new file mode 100644
index 0000000..5e4cd3b
--- /dev/null
+++ b/src/Conduit/Features/ConduitMapper.cs
@@ -0,0 +1,29 @@
+using Conduit.Domain;
+using Conduit.Features.Profiles;
+using Conduit.Features.Users;
+using Riok.Mapperly.Abstractions;
+
+namespace Conduit.Features;
+
+[Mapper]
+public partial class ConduitMapper
+{
+ [MapperIgnoreSource(nameof(Person.PersonId))]
+ [MapperIgnoreSource(nameof(Person.ArticleFavorites))]
+ [MapperIgnoreSource(nameof(Person.Following))]
+ [MapperIgnoreSource(nameof(Person.Followers))]
+ [MapperIgnoreSource(nameof(Person.Hash))]
+ [MapperIgnoreSource(nameof(Person.Salt))]
+ [MapperIgnoreTarget(nameof(User.Token))]
+ public partial User PersonToUser(Person person);
+
+ [MapperIgnoreSource(nameof(Person.PersonId))]
+ [MapperIgnoreSource(nameof(Person.Email))]
+ [MapperIgnoreSource(nameof(Person.ArticleFavorites))]
+ [MapperIgnoreSource(nameof(Person.Following))]
+ [MapperIgnoreSource(nameof(Person.Followers))]
+ [MapperIgnoreSource(nameof(Person.Hash))]
+ [MapperIgnoreSource(nameof(Person.Salt))]
+ [MapperIgnoreTarget(nameof(Profile.IsFollowed))]
+ public partial Profile PersonToProfile(Person person);
+}
diff --git a/src/Conduit/Features/Profiles/MappingProfile.cs b/src/Conduit/Features/Profiles/MappingProfile.cs
deleted file mode 100644
index 4c7e2d1..0000000
--- a/src/Conduit/Features/Profiles/MappingProfile.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using AutoMapper;
-
-namespace Conduit.Features.Profiles;
-
-public class MappingProfile : AutoMapper.Profile
-{
- public MappingProfile() => CreateMap(MemberList.None);
-}
diff --git a/src/Conduit/Features/Profiles/ProfileReader.cs b/src/Conduit/Features/Profiles/ProfileReader.cs
index cce9a95..b33fb41 100644
--- a/src/Conduit/Features/Profiles/ProfileReader.cs
+++ b/src/Conduit/Features/Profiles/ProfileReader.cs
@@ -2,7 +2,6 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using AutoMapper;
using Conduit.Infrastructure;
using Conduit.Infrastructure.Errors;
using Microsoft.EntityFrameworkCore;
@@ -12,7 +11,7 @@ namespace Conduit.Features.Profiles;
public class ProfileReader(
ConduitContext context,
ICurrentUserAccessor currentUserAccessor,
- IMapper mapper
+ ConduitMapper mapper
) : IProfileReader
{
public async Task ReadProfile(
@@ -34,7 +33,7 @@ CancellationToken cancellationToken
{
throw new RestException(HttpStatusCode.NotFound, new { User = Constants.NOT_FOUND });
}
- var profile = mapper.Map(person);
+ var profile = mapper.PersonToProfile(person);
if (currentUserName != null)
{
diff --git a/src/Conduit/Features/Users/Create.cs b/src/Conduit/Features/Users/Create.cs
index fe339c1..935c602 100644
--- a/src/Conduit/Features/Users/Create.cs
+++ b/src/Conduit/Features/Users/Create.cs
@@ -3,7 +3,6 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using AutoMapper;
using Conduit.Domain;
using Conduit.Infrastructure;
using Conduit.Infrastructure.Errors;
@@ -34,7 +33,7 @@ public class Handler(
ConduitContext context,
IPasswordHasher passwordHasher,
IJwtTokenGenerator jwtTokenGenerator,
- IMapper mapper
+ ConduitMapper mapper
) : IRequestHandler
{
public async Task Handle(Command message, CancellationToken cancellationToken)
@@ -78,7 +77,7 @@ await context
await context.Persons.AddAsync(person, cancellationToken);
await context.SaveChangesAsync(cancellationToken);
- var user = mapper.Map(person);
+ var user = mapper.PersonToUser(person);
user.Token = jwtTokenGenerator.CreateToken(
person.Username ?? throw new InvalidOperationException()
);
diff --git a/src/Conduit/Features/Users/Details.cs b/src/Conduit/Features/Users/Details.cs
index 9fe133c..3e6e51d 100644
--- a/src/Conduit/Features/Users/Details.cs
+++ b/src/Conduit/Features/Users/Details.cs
@@ -2,7 +2,6 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using AutoMapper;
using Conduit.Infrastructure;
using Conduit.Infrastructure.Errors;
using Conduit.Infrastructure.Security;
@@ -24,7 +23,7 @@ public class QueryValidator : AbstractValidator
public class QueryHandler(
ConduitContext context,
IJwtTokenGenerator jwtTokenGenerator,
- IMapper mapper
+ ConduitMapper mapper
) : IRequestHandler
{
public async Task Handle(Query message, CancellationToken cancellationToken)
@@ -41,7 +40,7 @@ public async Task Handle(Query message, CancellationToken cancella
);
}
- var user = mapper.Map(person);
+ var user = mapper.PersonToUser(person);
user.Token = jwtTokenGenerator.CreateToken(
person.Username ?? throw new InvalidOperationException()
);
diff --git a/src/Conduit/Features/Users/Edit.cs b/src/Conduit/Features/Users/Edit.cs
index 45a661e..58c4e6b 100644
--- a/src/Conduit/Features/Users/Edit.cs
+++ b/src/Conduit/Features/Users/Edit.cs
@@ -3,7 +3,6 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using AutoMapper;
using Conduit.Infrastructure;
using Conduit.Infrastructure.Errors;
using Conduit.Infrastructure.Security;
@@ -39,7 +38,7 @@ public class Handler(
ConduitContext context,
IPasswordHasher passwordHasher,
ICurrentUserAccessor currentUserAccessor,
- IMapper mapper
+ ConduitMapper mapper
) : IRequestHandler
{
public async Task Handle(Command message, CancellationToken cancellationToken)
@@ -70,7 +69,7 @@ public async Task Handle(Command message, CancellationToken cancel
await context.SaveChangesAsync(cancellationToken);
- return new UserEnvelope(mapper.Map(person));
+ return new UserEnvelope(mapper.PersonToUser(person));
}
}
}
diff --git a/src/Conduit/Features/Users/Login.cs b/src/Conduit/Features/Users/Login.cs
index 8b1a07a..cc202b6 100644
--- a/src/Conduit/Features/Users/Login.cs
+++ b/src/Conduit/Features/Users/Login.cs
@@ -3,7 +3,6 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using AutoMapper;
using Conduit.Infrastructure;
using Conduit.Infrastructure.Errors;
using Conduit.Infrastructure.Security;
@@ -38,7 +37,7 @@ public class Handler(
ConduitContext context,
IPasswordHasher passwordHasher,
IJwtTokenGenerator jwtTokenGenerator,
- IMapper mapper
+ ConduitMapper mapper
) : IRequestHandler
{
public async Task Handle(Command message, CancellationToken cancellationToken)
@@ -67,7 +66,7 @@ public async Task Handle(Command message, CancellationToken cancel
);
}
- var user = mapper.Map(person);
+ var user = mapper.PersonToUser(person);
user.Token = jwtTokenGenerator.CreateToken(
person.Username ?? throw new InvalidOperationException()
);
diff --git a/src/Conduit/Features/Users/MappingProfile.cs b/src/Conduit/Features/Users/MappingProfile.cs
deleted file mode 100644
index 3f289e4..0000000
--- a/src/Conduit/Features/Users/MappingProfile.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using AutoMapper;
-
-namespace Conduit.Features.Users;
-
-public class MappingProfile : Profile
-{
- public MappingProfile() => CreateMap(MemberList.None);
-}
diff --git a/src/Conduit/ServicesExtensions.cs b/src/Conduit/ServicesExtensions.cs
index 0ef3b46..50e0faf 100644
--- a/src/Conduit/ServicesExtensions.cs
+++ b/src/Conduit/ServicesExtensions.cs
@@ -32,7 +32,7 @@ public static void AddConduit(this IServiceCollection services)
services.AddValidatorsFromAssemblyContaining();
- services.AddAutoMapper(typeof(Program));
+ services.AddSingleton();
services.AddScoped();
services.AddScoped();
diff --git a/src/Conduit/packages.lock.json b/src/Conduit/packages.lock.json
index 236797c..6b07e4e 100644
--- a/src/Conduit/packages.lock.json
+++ b/src/Conduit/packages.lock.json
@@ -2,12 +2,6 @@
"version": 2,
"dependencies": {
"net10.0": {
- "AutoMapper": {
- "type": "Direct",
- "requested": "[14.0.0, )",
- "resolved": "14.0.0",
- "contentHash": "OC+1neAPM4oCCqQj3g2GJ2shziNNhOkxmNB9cVS8jtx4JbgmRzLcUOxB9Tsz6cVPHugdkHgCaCrTjjSI0Z5sCQ=="
- },
"FluentValidation": {
"type": "Direct",
"requested": "[12.0.0, )",
diff --git a/tests/Conduit.IntegrationTests/packages.lock.json b/tests/Conduit.IntegrationTests/packages.lock.json
index 78dd7a6..407d603 100644
--- a/tests/Conduit.IntegrationTests/packages.lock.json
+++ b/tests/Conduit.IntegrationTests/packages.lock.json
@@ -458,7 +458,6 @@
"conduit": {
"type": "Project",
"dependencies": {
- "AutoMapper": "[14.0.0, )",
"FluentValidation": "[12.0.0, )",
"FluentValidation.DependencyInjectionExtensions": "[12.0.0, )",
"MediatR": "[12.5.0, )",
@@ -466,21 +465,13 @@
"Microsoft.EntityFrameworkCore.InMemory": "[10.0.2, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[10.0.2, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[10.0.2, )",
+ "Riok.Mapperly": "[4.3.1, )",
"Serilog": "[4.3.0, )",
"Serilog.Extensions.Logging": "[8.0.0, )",
"Serilog.Sinks.Console": "[6.0.0, )",
"Swashbuckle.AspNetCore": "[9.0.6, )"
}
},
- "AutoMapper": {
- "type": "CentralTransitive",
- "requested": "[14.0.0, )",
- "resolved": "14.0.0",
- "contentHash": "OC+1neAPM4oCCqQj3g2GJ2shziNNhOkxmNB9cVS8jtx4JbgmRzLcUOxB9Tsz6cVPHugdkHgCaCrTjjSI0Z5sCQ==",
- "dependencies": {
- "Microsoft.Extensions.Options": "8.0.0"
- }
- },
"FluentValidation": {
"type": "CentralTransitive",
"requested": "[12.0.0, )",
@@ -555,6 +546,12 @@
"Microsoft.Extensions.Logging": "10.0.2"
}
},
+ "Riok.Mapperly": {
+ "type": "CentralTransitive",
+ "requested": "[4.3.1, )",
+ "resolved": "4.3.1",
+ "contentHash": "oSE+OVLHyiojrws0QVbQaAT2YKsYbyHqJXPdV7EhGseR/dYgeWS2x1T5qY5I4E+RJkJqMSG+FyZOq4SljWgJWQ=="
+ },
"Serilog": {
"type": "CentralTransitive",
"requested": "[4.3.0, )",
From a4cd4541206178ee703d6734e1fb85e78894aa17 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 16 Apr 2026 11:07:24 +0000
Subject: [PATCH 4/4] Merge master into PR branch and resolve conflicts
- Keep Mapperly, drop AutoMapper (including updated 16.1.1 from master)
- Pick up global.json SDK 10.0.201 update from master
- Pick up GitHub Actions workflow update from master
- Pick up SliceFixture.cs AddLogging() fix from master
- Remove redundant duplicate null check in ProfileReader.cs
Agent-Logs-Url: https://github.com/gothinkster/aspnetcore-realworld-example-app/sessions/00567f7d-07a5-41b8-b841-f2512c22ee3f
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
---
.github/workflows/dotnetcore.yml | 2 +-
global.json | 2 +-
src/Conduit/Features/Profiles/ProfileReader.cs | 5 -----
tests/Conduit.IntegrationTests/SliceFixture.cs | 1 +
4 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml
index 9853b1b..39b7ec0 100644
--- a/.github/workflows/dotnetcore.yml
+++ b/.github/workflows/dotnetcore.yml
@@ -11,5 +11,5 @@ jobs:
- uses: actions/checkout@v6
- uses: actions/setup-dotnet@v5
with:
- dotnet-version: 10.0.102
+ dotnet-version: 10.0.201
- run: dotnet run --project build/build.csproj
diff --git a/global.json b/global.json
index 3fca435..36a0730 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "10.0.102",
+ "version": "10.0.201",
"rollForward": "latestFeature"
}
}
diff --git a/src/Conduit/Features/Profiles/ProfileReader.cs b/src/Conduit/Features/Profiles/ProfileReader.cs
index b33fb41..465b663 100644
--- a/src/Conduit/Features/Profiles/ProfileReader.cs
+++ b/src/Conduit/Features/Profiles/ProfileReader.cs
@@ -28,11 +28,6 @@ CancellationToken cancellationToken
{
throw new RestException(HttpStatusCode.NotFound, new { User = Constants.NOT_FOUND });
}
-
- if (person == null)
- {
- throw new RestException(HttpStatusCode.NotFound, new { User = Constants.NOT_FOUND });
- }
var profile = mapper.PersonToProfile(person);
if (currentUserName != null)
diff --git a/tests/Conduit.IntegrationTests/SliceFixture.cs b/tests/Conduit.IntegrationTests/SliceFixture.cs
index f81d25a..9f43de2 100644
--- a/tests/Conduit.IntegrationTests/SliceFixture.cs
+++ b/tests/Conduit.IntegrationTests/SliceFixture.cs
@@ -17,6 +17,7 @@ public class SliceFixture : IDisposable
public SliceFixture()
{
var services = new ServiceCollection();
+ services.AddLogging();
services.AddConduit();
var builder = new DbContextOptionsBuilder();