diff --git a/Atc.SourceGenerators.slnx b/Atc.SourceGenerators.slnx
index 1f6f3d9..fd68529 100644
--- a/Atc.SourceGenerators.slnx
+++ b/Atc.SourceGenerators.slnx
@@ -19,6 +19,7 @@
+
diff --git a/docs/samples/Mapping.md b/docs/samples/Mapping.md
index 82e7378..79d85ac 100644
--- a/docs/samples/Mapping.md
+++ b/docs/samples/Mapping.md
@@ -16,14 +16,18 @@ This sample demonstrates the **MappingGenerator** in a realistic 3-layer archite
- **Atc.SourceGenerators.Mapping** - ASP.NET Core Minimal API (entry point)
- **Atc.SourceGenerators.Mapping.Domain** - Domain models and business logic
- **Atc.SourceGenerators.Mapping.DataAccess** - Data access layer with entities
+- **Atc.SourceGenerators.Mapping.Contract** - API contracts (DTOs)
## 🏗️ Architecture
```mermaid
-graph LR
+graph TB
subgraph "API Layer"
API[Minimal API Endpoints]
- DTO[DTOs: UserDto, AddressDto]
+ end
+
+ subgraph "Contract Layer"
+ DTO[DTOs: UserDto, AddressDto, UserStatusDto]
end
subgraph "Domain Layer"
@@ -36,32 +40,36 @@ graph LR
EE[Entity Enums: UserStatusEntity]
end
- subgraph "Generated Mappings"
- M1["UserEntity.MapToUser()"]
- M2["User.MapToUserDto()"]
- M3["AddressEntity.MapToAddress()"]
- M4["Address.MapToAddressDto()"]
+ subgraph "Generated Bidirectional Mappings"
+ M1["User ↔ UserEntity"]
+ M2["User → UserDto"]
+ M3["Address ↔ AddressEntity"]
+ M4["Address → AddressDto"]
+ M5["UserStatus ↔ UserStatusEntity"]
+ M6["UserStatus → UserStatusDto"]
end
- ENT -->|MapToUser| M1
- M1 --> DM
- DM -->|MapToUserDto| M2
- M2 --> DTO
-
- EE -.->|auto cast| DE
- DE -.->|auto cast| DTO
-
- ENT -.->|nested| M3
- M3 --> DM
- DM -.->|nested| M4
-
API --> DTO
- DTO --> API
+ DTO --> DM
+ DM --> M1
+ DM --> M2
+ DM --> M3
+ DM --> M4
+ DE --> M5
+ DE --> M6
+ M1 --> ENT
+ M3 --> ENT
+ M5 --> EE
+ M2 --> DTO
+ M4 --> DTO
+ M6 --> DTO
style M1 fill:#2ea44f
style M2 fill:#2ea44f
style M3 fill:#2ea44f
style M4 fill:#2ea44f
+ style M5 fill:#2ea44f
+ style M6 fill:#2ea44f
```
## 🔄 Mapping Flow
@@ -109,37 +117,42 @@ sequenceDiagram
### Data Access Layer
```csharp
-using Atc.SourceGenerators.Annotations;
-
-namespace Atc.SourceGenerators.Mapping.DataAccess;
+namespace Atc.SourceGenerators.Mapping.DataAccess.Entities;
-// Entity with mapping to Domain
-[MapTo(typeof(Domain.User))]
-public partial class UserEntity
+// Entity - NO mapping attribute (mapping defined in Domain)
+public class UserEntity
{
public int DatabaseId { get; set; }
public Guid Id { get; set; }
- public string Name { get; set; } = string.Empty;
+ public string FirstName { get; set; } = string.Empty;
+ public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public UserStatusEntity Status { get; set; }
public AddressEntity? Address { get; set; }
public DateTimeOffset CreatedAt { get; set; }
+ public DateTimeOffset? UpdatedAt { get; set; }
public bool IsDeleted { get; set; } // DB-specific field
+ public byte[] RowVersion { get; set; } = []; // DB-specific field
}
-[MapTo(typeof(Domain.Address))]
-public partial class AddressEntity
+public class AddressEntity
{
+ public int Id { get; set; }
public string Street { get; set; } = string.Empty;
public string City { get; set; } = string.Empty;
+ public string State { get; set; } = string.Empty;
public string PostalCode { get; set; } = string.Empty;
+ public string Country { get; set; } = string.Empty;
+ public DateTime CreatedAt { get; set; }
+ public DateTime? UpdatedAt { get; set; }
}
public enum UserStatusEntity
{
Active = 0,
Inactive = 1,
- Suspended = 2
+ Suspended = 2,
+ Deleted = 3
}
```
@@ -150,49 +163,76 @@ using Atc.SourceGenerators.Annotations;
namespace Atc.SourceGenerators.Mapping.Domain;
-// Domain model with mapping to DTO
+// Domain model with BIDIRECTIONAL mapping to Entity and forward mapping to DTO
[MapTo(typeof(UserDto))]
+[MapTo(typeof(UserEntity), Bidirectional = true)]
public partial class User
{
- public Guid Id { get; init; }
- public string Name { get; init; } = string.Empty;
- public string Email { get; init; } = string.Empty;
- public UserStatus Status { get; init; }
- public Address? Address { get; init; }
- public DateTimeOffset CreatedAt { get; init; }
+ public Guid Id { get; set; }
+ public string FirstName { get; set; } = string.Empty;
+ public string LastName { get; set; } = string.Empty;
+ public string Email { get; set; } = string.Empty;
+ public UserStatus Status { get; set; }
+ public Address? Address { get; set; }
+ public DateTimeOffset CreatedAt { get; set; }
+ public DateTimeOffset? UpdatedAt { get; set; }
}
[MapTo(typeof(AddressDto))]
+[MapTo(typeof(AddressEntity), Bidirectional = true)]
public partial class Address
{
- public string Street { get; init; } = string.Empty;
- public string City { get; init; } = string.Empty;
- public string PostalCode { get; init; } = string.Empty;
+ public string Street { get; set; } = string.Empty;
+ public string City { get; set; } = string.Empty;
+ public string State { get; set; } = string.Empty;
+ public string PostalCode { get; set; } = string.Empty;
+ public string Country { get; set; } = string.Empty;
}
+[MapTo(typeof(UserStatusDto))]
+[MapTo(typeof(UserStatusEntity), Bidirectional = true)]
public enum UserStatus
{
Active = 0,
Inactive = 1,
- Suspended = 2
+ Suspended = 2,
+ Deleted = 3
}
+```
+
+### Contract Layer
+
+```csharp
+namespace Atc.SourceGenerators.Mapping.Contract;
-// DTOs
+// DTOs - no mapping attributes needed (mapped from Domain)
public class UserDto
{
public Guid Id { get; set; }
- public string Name { get; set; } = string.Empty;
+ public string FirstName { get; set; } = string.Empty;
+ public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
- public string Status { get; set; } = string.Empty; // Different type!
+ public UserStatusDto Status { get; set; }
public AddressDto? Address { get; set; }
public DateTimeOffset CreatedAt { get; set; }
+ public DateTimeOffset? UpdatedAt { get; set; }
}
public class AddressDto
{
public string Street { get; set; } = string.Empty;
public string City { get; set; } = string.Empty;
+ public string State { get; set; } = string.Empty;
public string PostalCode { get; set; } = string.Empty;
+ public string Country { get; set; } = string.Empty;
+}
+
+public enum UserStatusDto
+{
+ Active = 0,
+ Inactive = 1,
+ Suspended = 2,
+ Deleted = 3
}
```
@@ -200,81 +240,82 @@ public class AddressDto
```csharp
using Atc.Mapping;
+using Atc.SourceGenerators.Mapping.Contract;
using Atc.SourceGenerators.Mapping.Domain;
using Microsoft.AspNetCore.Mvc;
-var app = WebApplication.Create();
+var builder = WebApplication.CreateBuilder(args);
+
+// Register services
+builder.Services.AddSingleton();
+builder.Services.AddSingleton();
-// POST endpoint - Create user
-app.MapPost("/users", async ([FromBody] CreateUserRequest request) =>
+var app = builder.Build();
+
+// GET endpoint - Retrieve user by ID
+app.MapGet("/users/{id:guid}", (Guid id, UserService userService) =>
{
- // Convert DTO → Domain
- var user = new User
+ var user = userService.GetById(id);
+ if (user is null)
{
- Id = Guid.NewGuid(),
- Name = request.Name,
- Email = request.Email,
- Status = UserStatus.Active,
- CreatedAt = DateTimeOffset.UtcNow
- };
-
- // Convert Domain → Entity
- var entity = user.MapToUserEntity();
- // Save to database...
-
- // Convert Domain → DTO for response
- var dto = user.MapToUserDto();
- return Results.Created($"/users/{user.Id}", dto);
-});
-
-// GET endpoint - Retrieve user
-app.MapGet("/users/{id:guid}", async (Guid id) =>
-{
- // Fetch from database
- var entity = await repository.GetByIdAsync(id);
- if (entity == null) return Results.NotFound();
-
- // Complete mapping chain: Entity → Domain → DTO
- var user = entity.MapToUser(); // Auto-converts enum, nested Address
- var dto = user.MapToUserDto(); // Auto-maps all properties
+ return Results.NotFound(new { message = $"User with ID {id} not found" });
+ }
- return Results.Ok(dto);
-});
+ // ✨ Use generated mapping: Domain → DTO
+ var data = user.MapToUserDto();
+ return Results.Ok(data);
+})
+.WithName("GetUserById")
+.Produces(StatusCodes.Status200OK)
+.Produces(StatusCodes.Status404NotFound);
-app.Run();
+// GET endpoint - Retrieve all users
+app.MapGet("/users", (UserService userService) =>
+{
+ // ✨ Use generated mapping: Domain → DTO
+ var data = userService
+ .GetAll()
+ .Select(u => u.MapToUserDto())
+ .ToList();
+ return Results.Ok(data);
+})
+.WithName("GetAllUsers")
+.Produces>(StatusCodes.Status200OK);
+
+await app.RunAsync();
```
## 📝 Generated Code
-The generator creates extension methods for each mapping:
+The generator creates extension methods for bidirectional mappings:
```csharp
//
namespace Atc.Mapping;
-public static partial class UserEntityExtensions
+// Bidirectional mapping: User ↔ UserEntity
+public static partial class UserExtensions
{
- public static Domain.User MapToUser(this UserEntity source)
+ public static UserEntity MapToUserEntity(this User source)
{
if (source is null)
{
return default!;
}
- return new Domain.User
+ return new UserEntity
{
Id = source.Id,
- Name = source.Name,
+ FirstName = source.FirstName,
+ LastName = source.LastName,
Email = source.Email,
- Status = (Domain.UserStatus)source.Status, // ✨ Auto enum conversion
- Address = source.Address?.MapToAddress()!, // ✨ Auto nested mapping
- CreatedAt = source.CreatedAt
+ Status = source.Status.MapToUserStatusEntity(), // ✨ Safe enum mapping (bidirectional)
+ Address = source.Address?.MapToAddressEntity()!, // ✨ Auto nested mapping
+ CreatedAt = source.CreatedAt,
+ UpdatedAt = source.UpdatedAt
};
}
-}
-public static partial class UserExtensions
-{
public static UserDto MapToUserDto(this User source)
{
if (source is null)
@@ -285,11 +326,37 @@ public static partial class UserExtensions
return new UserDto
{
Id = source.Id,
- Name = source.Name,
+ FirstName = source.FirstName,
+ LastName = source.LastName,
+ Email = source.Email,
+ Status = source.Status.MapToUserStatusDto(), // ✨ Safe enum mapping
+ Address = source.Address?.MapToAddressDto()!, // ✨ Nested mapping
+ CreatedAt = source.CreatedAt,
+ UpdatedAt = source.UpdatedAt
+ };
+ }
+}
+
+public static partial class UserEntityExtensions
+{
+ // Reverse mapping (from Bidirectional = true)
+ public static User MapToUser(this UserEntity source)
+ {
+ if (source is null)
+ {
+ return default!;
+ }
+
+ return new User
+ {
+ Id = source.Id,
+ FirstName = source.FirstName,
+ LastName = source.LastName,
Email = source.Email,
- Status = source.Status.ToString(), // ✨ Enum to string
- Address = source.Address?.MapToAddressDto()!, // ✨ Nested mapping
- CreatedAt = source.CreatedAt
+ Status = source.Status.MapToUserStatus(), // ✨ Safe enum mapping (reverse)
+ Address = source.Address?.MapToAddress()!, // ✨ Auto nested mapping
+ CreatedAt = source.CreatedAt,
+ UpdatedAt = source.UpdatedAt
};
}
}
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/ApiContracts/AddressDto.cs b/sample/Atc.SourceGenerators.Mapping.Contract/AddressDto.cs
similarity index 92%
rename from sample/Atc.SourceGenerators.Mapping.Domain/ApiContracts/AddressDto.cs
rename to sample/Atc.SourceGenerators.Mapping.Contract/AddressDto.cs
index f898d78..c72877a 100644
--- a/sample/Atc.SourceGenerators.Mapping.Domain/ApiContracts/AddressDto.cs
+++ b/sample/Atc.SourceGenerators.Mapping.Contract/AddressDto.cs
@@ -1,4 +1,4 @@
-namespace Atc.SourceGenerators.Mapping.Domain.ApiContracts;
+namespace Atc.SourceGenerators.Mapping.Contract;
///
/// Data transfer object for Address (used for API responses).
diff --git a/sample/Atc.SourceGenerators.Mapping.Contract/Atc.SourceGenerators.Mapping.Contract.csproj b/sample/Atc.SourceGenerators.Mapping.Contract/Atc.SourceGenerators.Mapping.Contract.csproj
new file mode 100644
index 0000000..9ed914b
--- /dev/null
+++ b/sample/Atc.SourceGenerators.Mapping.Contract/Atc.SourceGenerators.Mapping.Contract.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/ApiContracts/UserDto.cs b/sample/Atc.SourceGenerators.Mapping.Contract/UserDto.cs
similarity index 95%
rename from sample/Atc.SourceGenerators.Mapping.Domain/ApiContracts/UserDto.cs
rename to sample/Atc.SourceGenerators.Mapping.Contract/UserDto.cs
index 35712bc..c4e506b 100644
--- a/sample/Atc.SourceGenerators.Mapping.Domain/ApiContracts/UserDto.cs
+++ b/sample/Atc.SourceGenerators.Mapping.Contract/UserDto.cs
@@ -1,4 +1,4 @@
-namespace Atc.SourceGenerators.Mapping.Domain.ApiContracts;
+namespace Atc.SourceGenerators.Mapping.Contract;
///
/// Data transfer object for User (used for API responses).
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/ApiContracts/UserStatusDto.cs b/sample/Atc.SourceGenerators.Mapping.Contract/UserStatusDto.cs
similarity index 88%
rename from sample/Atc.SourceGenerators.Mapping.Domain/ApiContracts/UserStatusDto.cs
rename to sample/Atc.SourceGenerators.Mapping.Contract/UserStatusDto.cs
index 17dc803..fefaeb0 100644
--- a/sample/Atc.SourceGenerators.Mapping.Domain/ApiContracts/UserStatusDto.cs
+++ b/sample/Atc.SourceGenerators.Mapping.Contract/UserStatusDto.cs
@@ -1,4 +1,4 @@
-namespace Atc.SourceGenerators.Mapping.Domain.ApiContracts;
+namespace Atc.SourceGenerators.Mapping.Contract;
///
/// Data transfer object for UserStatus (used for API responses).
diff --git a/sample/Atc.SourceGenerators.Mapping.DataAccess/Atc.SourceGenerators.Mapping.DataAccess.csproj b/sample/Atc.SourceGenerators.Mapping.DataAccess/Atc.SourceGenerators.Mapping.DataAccess.csproj
index f511120..a6da9ce 100644
--- a/sample/Atc.SourceGenerators.Mapping.DataAccess/Atc.SourceGenerators.Mapping.DataAccess.csproj
+++ b/sample/Atc.SourceGenerators.Mapping.DataAccess/Atc.SourceGenerators.Mapping.DataAccess.csproj
@@ -4,13 +4,16 @@
net10.0
enable
enable
- $(NoWarn);CS0436;SA0001;CS1591;IDE0005
+ $(NoWarn);CS0436;IDE0005
+ false
+
+
+
+
-
-
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping.DataAccess/Entities/AddressEntity.cs b/sample/Atc.SourceGenerators.Mapping.DataAccess/Entities/AddressEntity.cs
index 42e55ce..8eab964 100644
--- a/sample/Atc.SourceGenerators.Mapping.DataAccess/Entities/AddressEntity.cs
+++ b/sample/Atc.SourceGenerators.Mapping.DataAccess/Entities/AddressEntity.cs
@@ -1,10 +1,9 @@
namespace Atc.SourceGenerators.Mapping.DataAccess.Entities;
///
-/// Database entity for address (maps to Domain.Address).
+/// Database entity for address.
///
-[MapTo(typeof(Address))]
-public partial class AddressEntity
+public class AddressEntity
{
///
/// Gets or sets the database ID.
diff --git a/sample/Atc.SourceGenerators.Mapping.DataAccess/Entities/UserEntity.cs b/sample/Atc.SourceGenerators.Mapping.DataAccess/Entities/UserEntity.cs
index d446131..ccb3c63 100644
--- a/sample/Atc.SourceGenerators.Mapping.DataAccess/Entities/UserEntity.cs
+++ b/sample/Atc.SourceGenerators.Mapping.DataAccess/Entities/UserEntity.cs
@@ -1,10 +1,9 @@
namespace Atc.SourceGenerators.Mapping.DataAccess.Entities;
///
-/// Database entity for user (maps to Domain.User).
+/// Database entity for user.
///
-[MapTo(typeof(User))]
-public partial class UserEntity
+public class UserEntity
{
///
/// Gets or sets the database ID (auto-increment).
diff --git a/sample/Atc.SourceGenerators.Mapping.DataAccess/GlobalUsings.cs b/sample/Atc.SourceGenerators.Mapping.DataAccess/GlobalUsings.cs
index 3e3b781..fc36262 100644
--- a/sample/Atc.SourceGenerators.Mapping.DataAccess/GlobalUsings.cs
+++ b/sample/Atc.SourceGenerators.Mapping.DataAccess/GlobalUsings.cs
@@ -1,5 +1,2 @@
-global using Atc.Mapping;
-
-global using Atc.SourceGenerators.Annotations;
global using Atc.SourceGenerators.Mapping.DataAccess.Entities;
-global using Atc.SourceGenerators.Mapping.Domain;
\ No newline at end of file
+global using Atc.SourceGenerators.Mapping.DataAccess.Repositories;
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping.DataAccess/Repositories/IUserRepository.cs b/sample/Atc.SourceGenerators.Mapping.DataAccess/Repositories/IUserRepository.cs
new file mode 100644
index 0000000..a699eef
--- /dev/null
+++ b/sample/Atc.SourceGenerators.Mapping.DataAccess/Repositories/IUserRepository.cs
@@ -0,0 +1,20 @@
+namespace Atc.SourceGenerators.Mapping.DataAccess.Repositories;
+
+///
+/// Repository interface for user operations.
+///
+public interface IUserRepository
+{
+ ///
+ /// Gets a user entity by ID.
+ ///
+ /// The user ID.
+ /// The user entity, or null if not found.
+ UserEntity? GetById(Guid id);
+
+ ///
+ /// Gets all user entities.
+ ///
+ /// Collection of user entities.
+ IEnumerable GetAll();
+}
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping.DataAccess/UserRepository.cs b/sample/Atc.SourceGenerators.Mapping.DataAccess/Repositories/UserRepository.cs
similarity index 87%
rename from sample/Atc.SourceGenerators.Mapping.DataAccess/UserRepository.cs
rename to sample/Atc.SourceGenerators.Mapping.DataAccess/Repositories/UserRepository.cs
index a115228..1590281 100644
--- a/sample/Atc.SourceGenerators.Mapping.DataAccess/UserRepository.cs
+++ b/sample/Atc.SourceGenerators.Mapping.DataAccess/Repositories/UserRepository.cs
@@ -1,4 +1,4 @@
-namespace Atc.SourceGenerators.Mapping.DataAccess;
+namespace Atc.SourceGenerators.Mapping.DataAccess.Repositories;
///
/// In-memory user repository for demo purposes.
@@ -62,19 +62,19 @@ public class UserRepository : IUserRepository
};
///
- /// Gets a user by ID.
+ /// Gets a user entity by ID.
///
/// The user ID.
- /// The user domain model, or null if not found.
- public User? GetById(Guid id)
+ /// The user entity, or null if not found.
+ public UserEntity? GetById(Guid id)
=> !users.TryGetValue(id, out var entity)
? null
- : entity.MapToUser();
+ : entity;
///
- /// Gets all users.
+ /// Gets all user entities.
///
- /// Collection of user domain models.
- public IEnumerable GetAll()
- => users.Values.Select(e => e.MapToUser());
+ /// Collection of user entities.
+ public IEnumerable GetAll()
+ => users.Values;
}
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/Address.cs b/sample/Atc.SourceGenerators.Mapping.Domain/Address.cs
index cd7dcc6..134889a 100644
--- a/sample/Atc.SourceGenerators.Mapping.Domain/Address.cs
+++ b/sample/Atc.SourceGenerators.Mapping.Domain/Address.cs
@@ -4,6 +4,7 @@ namespace Atc.SourceGenerators.Mapping.Domain;
/// Represents a physical address.
///
[MapTo(typeof(AddressDto))]
+[MapTo(typeof(AddressEntity), Bidirectional = true)]
public partial class Address
{
///
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/Atc.SourceGenerators.Mapping.Domain.csproj b/sample/Atc.SourceGenerators.Mapping.Domain/Atc.SourceGenerators.Mapping.Domain.csproj
index 33a5b0e..4332e79 100644
--- a/sample/Atc.SourceGenerators.Mapping.Domain/Atc.SourceGenerators.Mapping.Domain.csproj
+++ b/sample/Atc.SourceGenerators.Mapping.Domain/Atc.SourceGenerators.Mapping.Domain.csproj
@@ -1,19 +1,26 @@
+
+ net10.0
+ enable
+ enable
+ $(NoWarn);CS0436;IDE0005
+ false
+
+
+
+
+
+
+
+
+
-
-
- net10.0
- enable
- enable
- $(NoWarn);CS0436;SA0001;CS1591;IDE0005
-
-
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/GlobalUsings.cs b/sample/Atc.SourceGenerators.Mapping.Domain/GlobalUsings.cs
index 0604aea..b38e67c 100644
--- a/sample/Atc.SourceGenerators.Mapping.Domain/GlobalUsings.cs
+++ b/sample/Atc.SourceGenerators.Mapping.Domain/GlobalUsings.cs
@@ -1,3 +1,8 @@
+global using Atc.Mapping;
+
global using Atc.SourceGenerators.Annotations;
-global using Atc.SourceGenerators.Mapping.Domain.ApiContracts;
+global using Atc.SourceGenerators.Mapping.Contract;
+global using Atc.SourceGenerators.Mapping.DataAccess.Entities;
+global using Atc.SourceGenerators.Mapping.DataAccess.Repositories;
+
global using Microsoft.Extensions.DependencyInjection;
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/IUserRepository.cs b/sample/Atc.SourceGenerators.Mapping.Domain/IUserRepository.cs
deleted file mode 100644
index 8d836da..0000000
--- a/sample/Atc.SourceGenerators.Mapping.Domain/IUserRepository.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Atc.SourceGenerators.Mapping.Domain;
-
-///
-/// Repository interface for user operations.
-///
-public interface IUserRepository
-{
- ///
- /// Gets a user by ID.
- ///
- /// The user ID.
- /// The user domain model, or null if not found.
- User? GetById(Guid id);
-
- ///
- /// Gets all users.
- ///
- /// Collection of user domain models.
- IEnumerable GetAll();
-}
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/ServiceCollectionExtensions.cs b/sample/Atc.SourceGenerators.Mapping.Domain/ServiceCollectionExtensions.cs
deleted file mode 100644
index 4614908..0000000
--- a/sample/Atc.SourceGenerators.Mapping.Domain/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Atc.SourceGenerators.Mapping.Domain;
-
-///
-/// Extension methods for service collection registration.
-///
-public static class ServiceCollectionExtensions
-{
- ///
- /// Adds user services to the service collection.
- /// Note: You must register an IUserRepository implementation separately.
- ///
- /// The service collection.
- /// The service collection for chaining.
- public static IServiceCollection AddUserServices(
- this IServiceCollection services)
- {
- services.AddSingleton();
- return services;
- }
-}
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/User.cs b/sample/Atc.SourceGenerators.Mapping.Domain/User.cs
index 93353e3..402fa59 100644
--- a/sample/Atc.SourceGenerators.Mapping.Domain/User.cs
+++ b/sample/Atc.SourceGenerators.Mapping.Domain/User.cs
@@ -4,6 +4,7 @@ namespace Atc.SourceGenerators.Mapping.Domain;
/// Represents a user in the system.
///
[MapTo(typeof(UserDto))]
+[MapTo(typeof(UserEntity), Bidirectional = true)]
public partial class User
{
///
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/UserService.cs b/sample/Atc.SourceGenerators.Mapping.Domain/UserService.cs
index 4e8b7fd..f97aa96 100644
--- a/sample/Atc.SourceGenerators.Mapping.Domain/UserService.cs
+++ b/sample/Atc.SourceGenerators.Mapping.Domain/UserService.cs
@@ -22,12 +22,14 @@ public UserService(IUserRepository repository)
/// The user ID.
/// The user domain model, or null if not found.
public User? GetById(Guid id)
- => repository.GetById(id);
+ => repository.GetById(id)?.MapToUser();
///
/// Gets all users.
///
/// Collection of user domain models.
public IEnumerable GetAll()
- => repository.GetAll();
+ => repository
+ .GetAll()
+ .Select(e => e.MapToUser());
}
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping.Domain/UserStatus.cs b/sample/Atc.SourceGenerators.Mapping.Domain/UserStatus.cs
index e012cf5..9bbe077 100644
--- a/sample/Atc.SourceGenerators.Mapping.Domain/UserStatus.cs
+++ b/sample/Atc.SourceGenerators.Mapping.Domain/UserStatus.cs
@@ -3,6 +3,8 @@ namespace Atc.SourceGenerators.Mapping.Domain;
///
/// Represents the status of a user account.
///
+[MapTo(typeof(UserStatusDto))]
+[MapTo(typeof(UserStatusEntity), Bidirectional = true)]
public enum UserStatus
{
///
diff --git a/sample/Atc.SourceGenerators.Mapping/Atc.SourceGenerators.Mapping.csproj b/sample/Atc.SourceGenerators.Mapping/Atc.SourceGenerators.Mapping.csproj
index ac3e4e1..acbdb34 100644
--- a/sample/Atc.SourceGenerators.Mapping/Atc.SourceGenerators.Mapping.csproj
+++ b/sample/Atc.SourceGenerators.Mapping/Atc.SourceGenerators.Mapping.csproj
@@ -1,22 +1,37 @@
+
+ net10.0
+ enable
+ enable
+ $(NoWarn);CS0436;IDE0005
+
+
-
+
-
-
- net10.0
- enable
- enable
- $(NoWarn);CS0436;SA0001;CS1591;IDE0005
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping/GlobalUsings.cs b/sample/Atc.SourceGenerators.Mapping/GlobalUsings.cs
index 02b775c..d375cfc 100644
--- a/sample/Atc.SourceGenerators.Mapping/GlobalUsings.cs
+++ b/sample/Atc.SourceGenerators.Mapping/GlobalUsings.cs
@@ -1,7 +1,7 @@
global using Atc.Mapping;
-global using Atc.SourceGenerators.Mapping.DataAccess;
+global using Atc.SourceGenerators.Mapping.Contract;
+global using Atc.SourceGenerators.Mapping.DataAccess.Repositories;
global using Atc.SourceGenerators.Mapping.Domain;
-global using Atc.SourceGenerators.Mapping.Domain.ApiContracts;
global using Scalar.AspNetCore;
\ No newline at end of file
diff --git a/sample/Atc.SourceGenerators.Mapping/Program.cs b/sample/Atc.SourceGenerators.Mapping/Program.cs
index 9b0deb4..9d89a84 100644
--- a/sample/Atc.SourceGenerators.Mapping/Program.cs
+++ b/sample/Atc.SourceGenerators.Mapping/Program.cs
@@ -7,7 +7,7 @@
builder.Services.AddSingleton();
// Register domain services
-builder.Services.AddUserServices();
+builder.Services.AddSingleton();
// Add OpenAPI
builder.Services.AddOpenApi();
@@ -25,7 +25,9 @@
app
.MapGet("/", context =>
{
- if (context.RequestServices.GetRequiredService().IsDevelopment())
+ if (context.RequestServices
+ .GetRequiredService()
+ .IsDevelopment())
{
context.Response.Redirect("/scalar/v1");
return Task.CompletedTask;
diff --git a/sample/PetStore.Api/Program.cs b/sample/PetStore.Api/Program.cs
index 592e623..c80638b 100644
--- a/sample/PetStore.Api/Program.cs
+++ b/sample/PetStore.Api/Program.cs
@@ -3,7 +3,7 @@
var builder = WebApplication.CreateBuilder(args);
-// ✨ Scenario B: Register all services transitively (Domain + DataAccess)
+// ✨ Register all services transitively (Domain + DataAccess)
// This single call registers:
// - PetService from PetStore.Domain
// - PetRepository from PetStore.DataAccess (auto-detected as referenced assembly)
@@ -34,7 +34,9 @@
app
.MapGet("/", context =>
{
- if (context.RequestServices.GetRequiredService().IsDevelopment())
+ if (context.RequestServices
+ .GetRequiredService()
+ .IsDevelopment())
{
context.Response.Redirect("/scalar/v1");
return Task.CompletedTask;