Biblioteca leve e eficiente para mapeamento automático entre classes e DTOs em C#. Suporta mapeamento por convenção, atributos declarativos, API fluente e alta performance com Expression Trees compiladas.
$ dotnet add package MapFastMapLib é uma biblioteca leve e eficiente para mapeamento automático entre classes e DTOs (Data Transfer Objects) em C#. Inspirada nas melhores práticas de bibliotecas como AutoMapper, a MapLib oferece uma solução simples e poderosa para transferência de dados entre camadas da sua aplicação.
[MapTo], [MapFrom] e [IgnoreMap] para controle finoList<T>, Array, IEnumerable<T> automaticamentedotnet add package MapLib
git clone https://github.com/seu-usuario/MapLib.git
cd MapLib/MapLib
dotnet build
using MapLib;
// 1. Criar configuração
var config = new MapperConfiguration();
var mapper = config.CreateMapper();
// 2. Definir classes
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
// 3. Mapear!
var user = new User { Id = 1, Name = "João Silva", Email = "joao@example.com" };
var userDto = mapper.Map<User, UserDto>(user);
Console.WriteLine($"{userDto.Name} - {userDto.Email}");
// Output: João Silva - joao@example.com
O mapeamento por convenção funciona automaticamente para propriedades com o mesmo nome:
var config = new MapperConfiguration();
var mapper = config.CreateMapper();
var source = new Product { Id = 1, Name = "Notebook", Price = 3500.00m };
var destination = mapper.Map<Product, ProductDto>(source);
Use atributos para controlar o mapeamento de forma declarativa:
[MapTo] - Mapear para propriedade com nome diferentepublic class Product
{
public int Id { get; set; }
[MapTo("ProductName")] // Mapeia para ProductDto.ProductName
public string Name { get; set; }
}
public class ProductDto
{
public int Id { get; set; }
public string ProductName { get; set; }
}
[IgnoreMap] - Ignorar propriedade no mapeamentopublic class User
{
public int Id { get; set; }
public string Name { get; set; }
[IgnoreMap] // Não será mapeado
public string InternalCode { get; set; }
}
[MapFrom] - Especificar propriedade de origempublic class UserDto
{
public int Id { get; set; }
[MapFrom("Name")] // Mapeia de User.Name
public string UserName { get; set; }
}
Para mapeamentos mais complexos, use Profiles com API fluente:
using MapLib.Configuration;
public class UserProfile : MappingProfile
{
public UserProfile()
{
CreateMap<User, UserDto>()
// Combinar propriedades
.ForMember(dest => dest.FullName,
opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"))
// Calcular valores
.ForMember(dest => dest.Age,
opt => opt.MapFrom(src => CalculateAge(src.BirthDate)))
// Ignorar propriedades
.Ignore(dest => dest.InternalId);
}
private int CalculateAge(DateTime birthDate)
{
var today = DateTime.Today;
var age = today.Year - birthDate.Year;
if (birthDate.Date > today.AddYears(-age)) age--;
return age;
}
}
// Usar o profile
var config = new MapperConfiguration();
config.AddProfile<UserProfile>();
var mapper = config.CreateMapper();
MapLib mapeia coleções automaticamente:
using MapLib.Extensions;
var users = new List<User>
{
new User { Id = 1, Name = "Maria" },
new User { Id = 2, Name = "João" },
new User { Id = 3, Name = "Ana" }
};
// Método 1: Extension method
var userDtos = users.MapList<User, UserDto>(mapper);
// Método 2: Mapeamento direto
var userDtos2 = mapper.Map<List<User>, List<UserDto>>(users);
// Arrays também funcionam
User[] userArray = users.ToArray();
UserDto[] dtoArray = mapper.Map<User[], UserDto[]>(userArray);
Mapeamento recursivo de objetos complexos:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
public List<Order> Orders { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public AddressDto Address { get; set; }
public List<OrderDto> Orders { get; set; }
}
// MapLib mapeia recursivamente todos os objetos aninhados
var userDto = mapper.Map<User, UserDto>(user);
Classe principal para configurar o mapeador.
var config = new MapperConfiguration();
// Adicionar profile
config.AddProfile<MyProfile>();
config.AddProfile(new MyProfile());
// Criar mapper
IMapper mapper = config.CreateMapper();
// Validar configuração (futuro)
config.AssertConfigurationIsValid();
Interface principal para realizar mapeamentos.
// Criar novo objeto de destino
TDestination Map<TSource, TDestination>(TSource source);
// Mapear para objeto existente
TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
Classe base para criar perfis de mapeamento.
public class MyProfile : MappingProfile
{
public MyProfile()
{
CreateMap<Source, Destination>()
.ForMember(dest => dest.Property, opt => opt.MapFrom(src => src.OtherProperty))
.Ignore(dest => dest.IgnoredProperty)
.ConvertUsing(src => new Destination { /* custom logic */ });
}
}
Interface fluente para configurar mapeamentos.
CreateMap<Source, Destination>()
// Mapear membro específico
.ForMember(dest => dest.DestProperty, opt => opt.MapFrom(src => src.SourceProperty))
// Ignorar membro
.Ignore(dest => dest.IgnoredProperty)
// Usar conversor customizado
.ConvertUsing(src => new Destination { /* ... */ });
[MapTo(string propertyName)]Especifica o nome da propriedade de destino.
[MapFrom(string propertyName)]Especifica o nome da propriedade de origem.
[IgnoreMap]Marca a propriedade para ser ignorada no mapeamento.
using MapLib.Extensions;
// Mapear lista
List<TDestination> MapList<TSource, TDestination>(
this IEnumerable<TSource> source,
IMapper mapper);
// Mapear objeto genérico
TDestination MapTo<TDestination>(this object source, IMapper mapper);
// ❌ Não faça isso
public UserDto GetUser(int id)
{
var config = new MapperConfiguration(); // Cria nova config a cada chamada
var mapper = config.CreateMapper();
return mapper.Map<User, UserDto>(user);
}
// ✅ Faça isso
public class UserService
{
private readonly IMapper _mapper;
public UserService(IMapper mapper) // Injete via DI
{
_mapper = mapper;
}
public UserDto GetUser(int id)
{
return _mapper.Map<User, UserDto>(user);
}
}
// ✅ Bom: Agrupe mapeamentos relacionados
public class UserMappingProfile : MappingProfile
{
public UserMappingProfile()
{
CreateMap<User, UserDto>();
CreateMap<User, UserSummaryDto>();
CreateMap<UserCreateRequest, User>();
}
}
public class OrderMappingProfile : MappingProfile
{
public OrderMappingProfile()
{
CreateMap<Order, OrderDto>();
CreateMap<OrderItem, OrderItemDto>();
}
}
// ✅ Bom: DTO flat e simples
public class UserDto
{
public int Id { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
// ❌ Evite: Lógica de negócio em DTOs
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public void ValidateEmail() { /* ... */ } // Não!
public decimal CalculateDiscount() { /* ... */ } // Não!
}
// ✅ Bom: Mapeamento direto
CreateMap<User, UserDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"));
// ❌ Evite: Lógica complexa no mapeamento
CreateMap<Order, OrderDto>()
.ForMember(dest => dest.Total, opt => opt.MapFrom(src =>
{
// Muita lógica aqui...
var subtotal = src.Items.Sum(x => x.Price * x.Quantity);
var discount = src.DiscountPercentage / 100m * subtotal;
var tax = (subtotal - discount) * 0.15m;
return subtotal - discount + tax + src.ShippingCost;
}));
// Melhor: Calcule isso no modelo de domínio
public class TemperatureProfile : MappingProfile
{
public TemperatureProfile()
{
CreateMap<Temperature, TemperatureDto>()
.ConvertUsing(src => new TemperatureDto
{
Fahrenheit = src.Celsius * 9 / 5 + 32
});
}
}
public class UserProfile : MappingProfile
{
public UserProfile()
{
CreateMap<User, UserDto>()
.ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src =>
string.IsNullOrEmpty(src.NickName) ? src.FullName : src.NickName))
.ForMember(dest => dest.Status, opt => opt.MapFrom(src =>
src.IsActive ? "Ativo" : "Inativo"));
}
}
public class ProductProfile : MappingProfile
{
public ProductProfile()
{
// Produto -> DTO
CreateMap<Product, ProductDto>();
// DTO -> Produto
CreateMap<ProductDto, Product>();
}
}
// Startup.cs ou Program.cs
public void ConfigureServices(IServiceCollection services)
{
// Criar configuração global
var mapperConfig = new MapperConfiguration();
mapperConfig.AddProfile<UserProfile>();
mapperConfig.AddProfile<OrderProfile>();
// Registrar como singleton
var mapper = mapperConfig.CreateMapper();
services.AddSingleton<IMapper>(mapper);
}
// Controller
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IMapper _mapper;
private readonly IUserRepository _repository;
public UsersController(IMapper mapper, IUserRepository repository)
{
_mapper = mapper;
_repository = repository;
}
[HttpGet("{id}")]
public ActionResult<UserDto> GetUser(int id)
{
var user = _repository.GetById(id);
if (user == null) return NotFound();
return _mapper.Map<User, UserDto>(user);
}
[HttpPost]
public ActionResult<UserDto> CreateUser(CreateUserRequest request)
{
var user = _mapper.Map<CreateUserRequest, User>(request);
_repository.Add(user);
return CreatedAtAction(nameof(GetUser),
new { id = user.Id },
_mapper.Map<User, UserDto>(user));
}
}
Este projeto está licenciado sob a licença MIT - veja o arquivo LICENSE para detalhes.
Contribuições são bem-vindas! Sinta-se à vontade para abrir issues e pull requests.
Para questões e suporte, abra uma issue no GitHub.
MapLib - Mapeamento de DTOs simples e eficiente para C# 🚀