Myth.Morph
3.0.3
dotnet add package Myth.Morph --version 3.0.3
NuGet\Install-Package Myth.Morph -Version 3.0.3
<PackageReference Include="Myth.Morph" Version="3.0.3" />
<PackageVersion Include="Myth.Morph" Version="3.0.3" />
<PackageReference Include="Myth.Morph" />
paket add Myth.Morph --version 3.0.3
#r "nuget: Myth.Morph, 3.0.3"
#:package Myth.Morph@3.0.3
#addin nuget:?package=Myth.Morph&version=3.0.3
#tool nuget:?package=Myth.Morph&version=3.0.3
Myth.Morph
A powerful .NET library for object transformation and mapping. Myth.Morph provides a flexible and extensible system for converting between different types with support for both convention-based and custom mappings.
The main goal is to simplify object mapping scenarios while providing high flexibility and performance through dependency injection integration and schema-based configuration.
⭐ Features
- Simple and Intuitive: Easy-to-use extension methods for object transformation
- Flexible Mapping: Support for automatic, custom, and instance-based mappings
- Dependency Injection Integration: Full integration with Microsoft.Extensions.DependencyInjection
- Generic Type Support: Automatic mapping for generic collections and interfaces
- Asynchronous Operations: Built-in support for async property binding
- Logging Integration: Comprehensive logging through Microsoft.Extensions.Logging
- Exception Safety: Detailed exception handling with custom exception types
- Schema-Based Configuration: Fluent API for configuring complex mappings
🕶️ Using
🚀 Quick Start
Installation and Setup
First, register Myth.Morph in your dependency injection container:
services.AddMorph();
Basic Usage
Transform objects using the extension methods:
// Simple transformation
var destination = source.To<DestinationType>();
// Transform with custom service provider
var destination = source.To<DestinationType>(serviceProvider);
// Transform collections
var destinationList = sourceList.To<DestinationType>();
// Async transformation
var destination = await source.ToAsync<DestinationType>();
Check if Mapping Exists
// Check if a mapping exists
bool canMap = source.CanBindTo<DestinationType>();
// Type-safe checking
bool canMap = source.CanBindTo<SourceType, DestinationType>();
📋 Instance-Based Mapping
Create custom mappings by implementing the IMorphable<TDestination>
interface:
public class UserDto : IMorphable<User>
{
public string Name { get; set; }
public string Email { get; set; }
public DateTime BirthDate { get; set; }
public void MorphTo(Schema<User> schema)
{
schema
.Bind(u => u.FullName, () => Name)
.Bind(u => u.EmailAddress, () => Email)
.Bind(u => u.Age, sp => CalculateAge(BirthDate))
.BindAsync(u => u.Profile, async sp =>
{
var profileService = sp.GetService<IProfileService>();
return await profileService.GetProfileAsync(Email);
})
.Ignore(u => u.InternalId);
}
private int CalculateAge(DateTime birthDate)
=> DateTime.Today.Year - birthDate.Year;
}
⚙️ Advanced Schema Configuration
Synchronous Bindings
public void MorphTo(Schema<Destination> schema)
{
// Bind with service provider resolver
schema.Bind(d => d.Property, sp =>
{
var service = sp.GetService<IMyService>();
return service.GetValue();
});
// Bind with direct resolver
schema.Bind(d => d.Property, () => "Direct Value");
// Ignore properties
schema.Ignore(d => d.UnwantedProperty);
}
Asynchronous Bindings
public void MorphTo(Schema<Destination> schema)
{
// Async binding with service provider
schema.BindAsync(d => d.AsyncProperty, async sp =>
{
var service = sp.GetService<IAsyncService>();
return await service.GetDataAsync();
});
// Async binding with direct resolver
schema.BindAsync(d => d.AsyncProperty, async () =>
{
await Task.Delay(100);
return "Async Value";
});
}
🔧 Configuration Options
Assembly Configuration
services.AddMorph(settings =>
{
// Add specific assemblies
settings.AddAssembly(Assembly.GetExecutingAssembly());
settings.AddAssemblies(assembly1, assembly2);
// Clear and add custom assemblies
settings.ClearAssemblies()
.AddAssembly(customAssembly);
});
Generic Type Mappings
services.AddMorph(settings =>
{
// Add custom interface to concrete mappings
settings.AddGenericMorph(typeof(IMyInterface<>), typeof(MyImplementation<>));
// Type-safe generic mapping
settings.AddGenericMapping<ICustomCollection<>, CustomCollection<>>();
// Clear default mappings and add custom ones
settings.ClearGenericMappings()
.AddGenericMapping<IList<>, ArrayList>();
});
Default Generic Mappings
The library includes these default mappings:
IList<>
→List<>
ICollection<>
→List<>
IDictionary<,>
→Dictionary<,>
ISet<>
→HashSet<>
IReadOnlyCollection<>
→ReadOnlyCollection<>
IReadOnlyList<>
→List<>
IReadOnlySet<>
→HashSet<>
🏗️ Complex Mapping Examples
Repository Pattern Integration
public class UserService
{
private readonly IServiceProvider _serviceProvider;
public UserService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task<UserDto> GetUserAsync(int userId)
{
var user = await GetUserFromDatabase(userId);
return user.To<UserDto>(_serviceProvider);
}
public async Task<IEnumerable<UserDto>> GetUsersAsync()
{
var users = await GetUsersFromDatabase();
return await users.ToAsync<UserDto>(_serviceProvider);
}
}
Complex Object Transformation
public class OrderDto : IMorphable<Order>
{
public int Id { get; set; }
public string CustomerName { get; set; }
public List<OrderItemDto> Items { get; set; }
public decimal TotalAmount { get; set; }
public void MorphTo(Schema<Order> schema)
{
schema
.Bind(o => o.OrderId, () => Id)
.Bind(o => o.Customer, sp =>
{
var customerService = sp.GetService<ICustomerService>();
return customerService.GetCustomerByName(CustomerName);
})
.BindAsync(o => o.OrderItems, async sp =>
{
// Transform collection asynchronously
return await Items.ToAsync<OrderItem>(sp);
})
.Bind(o => o.Total, () => TotalAmount)
.BindAsync(o => o.ShippingInfo, async sp =>
{
var shippingService = sp.GetService<IShippingService>();
return await shippingService.CalculateShippingAsync(Id);
})
.Ignore(o => o.InternalNotes);
}
}
🎯 Use Cases
API Response Transformation
// Transform API responses to domain models
public async Task<User> GetUserFromApi(int userId)
{
var apiResponse = await httpClient.GetAsync($"users/{userId}");
var userDto = await apiResponse.Content.ReadFromJsonAsync<UserApiDto>();
return userDto.To<User>();
}
Database Entity Mapping
// Transform database entities to DTOs
public async Task<IEnumerable<ProductDto>> GetProductsAsync()
{
var entities = await dbContext.Products.ToListAsync();
return entities.To<ProductDto>();
}
Event Sourcing Integration
public class UserCreatedEvent : IMorphable<User>
{
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime CreatedAt { get; set; }
public void MorphTo(Schema<User> schema)
{
schema
.Bind(u => u.Id, () => UserId)
.Bind(u => u.FullName, () => Name)
.Bind(u => u.EmailAddress, () => Email)
.Bind(u => u.CreatedDate, () => CreatedAt)
.Bind(u => u.IsActive, () => true);
}
}
🚨 Exception Handling
The library provides detailed exception handling:
Exception Types
BinderNotFoundException
: Thrown when no mapping exists between source and destination typesBindException
: Thrown when property or field binding operations failInvalidMorphConfigurationException
: Thrown when the Morph system is not properly configured
Example Exception Handling
try
{
var result = source.To<DestinationType>();
}
catch (BinderNotFoundException ex)
{
// Handle missing mapping
logger.LogError($"No mapping found: {ex.Message}");
}
catch (BindException ex)
{
// Handle binding error
logger.LogError($"Binding failed: {ex.Message}");
}
catch (InvalidMorphConfigurationException ex)
{
// Handle configuration error
logger.LogError($"Configuration issue: {ex.Message}");
}
📊 Performance Tips
- Reuse Service Provider: Pass the same service provider instance when transforming multiple objects
- Collection Transformation: Use type-specific collection methods for better performance
- Assembly Scanning: Limit assemblies in configuration to reduce startup time
- Async Operations: Use async methods for I/O-bound operations in bindings
🛠️ Troubleshooting
Common Issues
"ServiceProvider not configured" Error
// Ensure AddMorph() is called in DI configuration
services.AddMorph();
"No mapping found" Error
// Check if the source type implements IMorphable<TDestination>
public class MySource : IMorphable<MyDestination>
{
public void MorphTo(Schema<MyDestination> schema) { /* implementation */ }
}
Generic Collection Mapping Issues
// Register appropriate generic mappings
services.AddMorph(settings =>
{
settings.AddGenericMapping<IMyCollection<>, MyCollection<>>();
});
📝 Contributing
We welcome contributions! Please read our contributing guidelines and feel free to submit pull requests.
📄 License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
3.0.3 | 168 | 8/30/2025 |
3.0.2 | 74 | 8/23/2025 |
3.0.2-preview.4 | 113 | 8/21/2025 |
3.0.2-preview.3 | 96 | 8/16/2025 |