Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 48 additions & 62 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ Follows a common-sense semantic versioning pattern:
```bash
dotnet add package CoreMap
```
# 🚀 Usage

## 🚀 Usage

### 1. Create Entities
## 1. Create Entities

```csharp
public record ArticleEntity
Expand All @@ -92,10 +91,11 @@ public record AuthorEntity
{
public Guid Id { get; init; }
public string Name { get; init; } = default!;

}
```
### 2. Create Responses (DTOs)

## 2. Create Responses (DTOs)

```csharp
internal record ArticleResponse
{
Expand All @@ -104,14 +104,16 @@ internal record ArticleResponse
public string Description { get; init; } = default!;
public AuthorResponse Author { get; init; } = default!;
}

internal class AuthorResponse
{
public Guid Id { get; init; }
public string Title { get; init; } = default!;
public string Description { get; init; } = default!;
}
```
### 3. Create Handlers

# 3. Create Mapping Handlers

```csharp
internal class AuthorResponseToEntityMap : ICoreMapHandler<AuthorResponse, AuthorEntity>
Expand All @@ -122,6 +124,7 @@ internal class AuthorResponseToEntityMap : ICoreMapHandler<AuthorResponse, Autho
Name = data.Title
};
}

internal class ArticleResponseToEntityMap : ICoreMapHandler<ArticleResponse, ArticleEntity>
{
public ArticleEntity Handler(ArticleResponse data, ICoreMap alsoMap) => new ArticleEntity()
Expand All @@ -131,74 +134,57 @@ internal class ArticleResponseToEntityMap : ICoreMapHandler<ArticleResponse, Art
Title = data.Title,
WrittenBy = alsoMap.MapTo<AuthorResponse, AuthorEntity>(data.Author)
};

}
```

### 4. Register Services
In your Startup or whereever you define your dependency injection:
# 4. Register Services

```csharp
public static IServiceCollection AddServices(IServiceCollection services){
// For Assembly Scanned Registration
services.AddCoreMap(o => { }, new Type[]{
typeof(Startup)
});
public static IServiceCollection AddServices(IServiceCollection services)
{
// Assembly-scanned registration
services.AddCoreMap(o => { }, new Type[]
{
typeof(Startup)
});

// Or manual registration
services.AddCoreMap(o => { });
services.AddScoped<ICoreMapHandler<ArticleResponse, ArticleEntity>, ArticleResponseToEntityMap>();
services.AddScoped<ICoreMapHandler<AuthorResponse, AuthorEntity>, AuthorResponseToEntityMap>();

return services;
}
```

// For Manually Registration
services.AddCoreMap(o => { });
services.AddScoped<ICoreMapHandler<ArticleResponse, ArticleEntity>, ArticleResponseToEntityMap>();
services.AddScoped<ICoreMapHandler<AuthorResponse, AuthorEntity>, AuthorResponseToEntityMap>();
return service;
}
# 5. Mapping Examples

## Fluent Mapping (Preferred)

### Map a single object

```csharp
ArticleEntity item = coreMap.Map(response).To<ArticleEntity>();
```

### 4. Use Mapping
Example:
### Map a collection

```csharp
private ArticleEntity Item { get; set; } = default!;
protected override void OnInitialized(IServiceProvider serviceP)
{
var coreMap = serviceP.GetRequiredService<ICoreMap>();
var response = new ArticleResponse()
{
Description = "A description",
Title = "A Title",
Id = Guid.NewGuid(),
Author = new AuthorResponse()
{
Id = Guid.NewGuid(),
Title = "Maksim Shimshon"
}
};
ICollection<ArticleEntity> entities =
coreMap.MapEach(responses).To<ArticleEntity>();
```

Item = coreMap.MapTo<ArticleResponse, ArticleEntity>(responses);
}
## Traditional Mapping (Still Supported)

### Map a single object

```csharp
ArticleEntity item = coreMap.MapTo<ArticleResponse, ArticleEntity>(response);
```

Map a collection:
### Map a collection

```csharp
private ArticleEntity Item { get; set; } = default!;
protected override void OnInitialized(IServiceProvider serviceP)
{
var coreMap = serviceP.GetRequiredService<ICoreMap>();
var response = new ArticleResponse()
{
Description = "A description",
Title = "A Title",
Id = Guid.NewGuid(),
Author = new AuthorResponse()
{
Id = Guid.NewGuid(),
Title = "Maksim Shimshon"
}
};
List<ArticleResponse> responses = new()
{
response with { },
response with{ }
};
ICollection<ArticleEntity> entities = coreMap.MapEachTo<ArticleResponse, ArticleEntity>(responses);
}
ICollection<ArticleEntity> entities =
coreMap.MapEachTo<ArticleResponse, ArticleEntity>(responses);
```
13 changes: 13 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
README: https://github.com/mshimshon/CoreMap

# v1.1.0
## New Features
- Added Fluent Mapping support for both single and collection mappings
- Introduced Map() builder for fluent single-object mapping
- Introduced MapEach() builder for fluent collection mapping

## Improvements
- CoreMapper internally updated to support fluent builders
- Infrastructure prepared for future fluent extensions

## Unchanged
- Traditional MapTo and MapEachTo methods remain fully supported

# v1.0.0
- Added Support for .NET 10

Expand Down
4 changes: 2 additions & 2 deletions src/CoreMap/CoreMap.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFrameworks>net10.0;net6.0;net8.0;netstandard2.1;netstandard2.0;</TargetFrameworks>
<LangVersion>10</LangVersion>
<LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
Expand All @@ -17,7 +17,7 @@
<PackageReadmeFile>RELEASES.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/mshimshon/CoreMap/tree/main</RepositoryUrl>
<PackageTags>mapping, object-mapper, dependency-injection, clean-architecture, dotnet, csharp, async, manual-mapping, design-patterns, software-architecture, lightweight</PackageTags>
<Version>1.0.0</Version>
<Version>1.1.0</Version>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<PackageIcon>icon.png</PackageIcon>
Expand Down
1 change: 1 addition & 0 deletions src/CoreMap/CoreMapConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ namespace CoreMap;
public class CoreMapConfiguration
{
public ServiceScope Scope { get; set; } = ServiceScope.Scoped;

}
20 changes: 20 additions & 0 deletions src/CoreMap/Engine/CoreMapBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace CoreMap.Engine;

internal class CoreMapBuilder<TOrigin> : ICoreMapBuilder where TOrigin : class
{
private readonly TOrigin _origin;
private readonly ICoreMap _coreMap;

public CoreMapBuilder(TOrigin origin, ICoreMap coreMap)
{
_origin = origin;
_coreMap = coreMap;
}

public TDestination To<TDestination>() where TDestination : class
=> _coreMap.MapTo<TOrigin, TDestination>(_origin);
}
20 changes: 20 additions & 0 deletions src/CoreMap/Engine/CoreMapListBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace CoreMap.Engine;

internal class CoreMapListBuilder<TOrigin> : ICoreMapListBuilder where TOrigin : class
{
private readonly ICollection<TOrigin> _origin;
private readonly ICoreMap _coreMap;

public CoreMapListBuilder(ICollection<TOrigin> origin, ICoreMap coreMap)
{
_origin = origin;
_coreMap = coreMap;
}

public ICollection<TDestination> To<TDestination>() where TDestination : class
=> _coreMap.MapEachTo<TOrigin, TDestination>(_origin);
}
14 changes: 12 additions & 2 deletions src/CoreMap/Engine/CoreMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,21 @@ public TDestination MapTo<TOrigin, TDestination>(TOrigin origin)
public ICollection<TDestination> MapEachTo<TOrigin, TDestination>(ICollection<TOrigin> origins)
=> origins.Select(MapTo<TOrigin, TDestination>).ToList();



private ICoreMapHandler<TOrigin, TDestination> GetService<TOrigin, TDestination>()
{
return _serviceProvider.GetRequiredService<ICoreMapHandler<TOrigin, TDestination>>();
}

public ICoreMapBuilder Map<TOrigin>(TOrigin origin) where TOrigin : class
{
var builder = new CoreMapBuilder<TOrigin>(origin, this);
return builder;
}

public ICoreMapListBuilder MapEach<TOrigin>(ICollection<TOrigin> origins)
where TOrigin : class
{
var builder = new CoreMapListBuilder<TOrigin>(origins, this);
return builder;
}
}
6 changes: 6 additions & 0 deletions src/CoreMap/ICoreMap.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
namespace CoreMap;
public interface ICoreMap
{

ICoreMapBuilder Map<TOrigin>(TOrigin origin)
where TOrigin : class;

TDestination MapTo<TOrigin, TDestination>(TOrigin origin);
ICollection<TDestination> MapEachTo<TOrigin, TDestination>(ICollection<TOrigin> origins);
ICoreMapListBuilder MapEach<TOrigin>(ICollection<TOrigin> origins)
where TOrigin : class;
}
12 changes: 12 additions & 0 deletions src/CoreMap/ICoreMapBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace CoreMap;

public interface ICoreMapBuilder
{
TDestination To<TDestination>() where TDestination : class;


}
12 changes: 12 additions & 0 deletions src/CoreMap/ICoreMapListBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace CoreMap;

public interface ICoreMapListBuilder
{
ICollection<TDestination> To<TDestination>() where TDestination : class;


}
56 changes: 56 additions & 0 deletions tests/CoreMap.Tests/MappingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,28 @@ public void RequestShouldProperlyConvert()
Assert.Equal(entity.WrittenBy.Id, createRequest.AuthorId);
}

[Fact]
public void RequestShouldProperlyConvert_UsingFluent()
{
var coreMap = ServiceProvider.GetRequiredService<ICoreMap>();
var entity = new ArticleEntity()
{
Description = "A description",
Title = "A Title",
Id = Guid.NewGuid(),
WrittenBy = new AuthorEntity()
{
Id = Guid.NewGuid(),
Name = "Maksim Shimshon"
}
};

var createRequest2 = coreMap.Map(entity).To<CreateArticleRequest>();
Assert.Equal(entity.Title, createRequest2.Title);
Assert.Equal(entity.Description, createRequest2.Description);
Assert.Equal(entity.WrittenBy.Id, createRequest2.AuthorId);
}

[Fact]
public void ArrayConverter_ShouldConvertAll()
{
Expand Down Expand Up @@ -58,5 +80,39 @@ response with{ }
Assert.Equal(response.Author.Title, p.WrittenBy.Name);
Assert.Equal(response.Author.Id, p.WrittenBy.Id);
});

}

[Fact]
public void ArrayConverter_ShouldConvertAll_UsingFluent()
{
var coreMap = ServiceProvider.GetRequiredService<ICoreMap>();
var response = new ArticleResponse()
{
Description = "A description",
Title = "A Title",
Id = Guid.NewGuid(),
Author = new AuthorResponse()
{
Id = Guid.NewGuid(),
Title = "Maksim Shimshon"
}
};
List<ArticleResponse> responses = new()
{
response with { },
response with{ }
};


ICollection<ArticleEntity> entities = coreMap.MapEach(responses).To<ArticleEntity>();
Assert.All(entities, p =>
{
Assert.Equal(response.Title, p.Title);
Assert.Equal(response.Description, p.Description);
Assert.Equal(response.Id, p.Id);
Assert.Equal(response.Author.Title, p.WrittenBy.Name);
Assert.Equal(response.Author.Id, p.WrittenBy.Id);
});
}
}