.NET Framework 4.8 ASP.NET MVC 5 Mapperly AutoMapper Source Generator
Đây là phần 2 trong series modernize ASP.NET MVC 5 trên .NET Framework 4.8.
Ở Part 1, chúng ta đã migrate project sang SDK-style .csproj.
Bây giờ nền móng đã sạch, có thể bắt đầu tối ưu phần “đụng nhiều nhất mỗi ngày”: mapping giữa entity và ViewModel.
1. Vì sao nên thay AutoMapper?
AutoMapper rất tiện — nhưng cũng có những trade-off:
- Mapping diễn ra ở runtime → khó debug
- Logic bị “giấu” trong config
- Performance không tối ưu trong một số case
Khi project lớn dần, những vấn đề này bắt đầu rõ ràng hơn.
Giải pháp thay thế: compile-time mapping.
Một trong những thư viện nổi bật: Mapperly.
2. Mapperly là gì?
Mapperly sử dụng Source Generator để generate code mapping ngay lúc build.
Nghĩa là:
- Không còn reflection
- Không còn runtime mapping
- Mapping = code C# thật (có thể debug)
Hiểu đơn giản: thay vì “config mapping”, bạn “viết mapping — và tool generate phần còn lại”.
3. Cài đặt Mapperly
Install-Package Riok.Mapperly
Quan trọng: đây là source generator → chạy lúc compile.
4. Domain và ViewModel
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
public DateTime CreatedAt { get; set; }
public Category Category { get; set; }
}
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string PriceDisplay { get; set; }
public string StockStatus { get; set; }
public string CategoryName { get; set; }
public string CreatedAtDisplay { get; set; }
}
5. AutoMapper (trước khi migrate)
CreateMap<Product, ProductViewModel>()
.ForMember(d => d.CategoryName,
opt => opt.MapFrom(s => s.Category.Name))
.ForMember(d => d.PriceDisplay,
opt => opt.MapFrom(s =>
s.Price.ToString("N0") + " ₫"))
.ForMember(d => d.StockStatus,
opt => opt.MapFrom(s =>
s.Stock > 0 ? "In stock" : "Out of stock"))
.ForMember(d => d.CreatedAtDisplay,
opt => opt.MapFrom(s =>
s.CreatedAt.ToString("dd/MM/yyyy")));
Vấn đề: logic mapping bị “giấu” trong config.
6. Mapperly (sau khi migrate)
Tạo mapper:
using Riok.Mapperly.Abstractions;
[Mapper]
public partial class ProductMapper
{
public partial ProductViewModel ToDto(Product product);
private string MapPriceDisplay(decimal price)
=> price.ToString("N0") + " ₫";
private string MapStockStatus(int stock)
=> stock > 0 ? "In stock" : "Out of stock";
private string MapCreatedAtDisplay(DateTime createdAt)
=> createdAt.ToString("dd/MM/yyyy");
}
Mapperly sẽ tự generate code tương đương:
// generated (simplified)
public partial class ProductMapper
{
public partial ProductViewModel ToDto(Product product)
{
return new ProductViewModel
{
Id = product.Id,
Name = product.Name,
CategoryName = product.Category.Name,
PriceDisplay = MapPriceDisplay(product.Price),
StockStatus = MapStockStatus(product.Stock),
CreatedAtDisplay = MapCreatedAtDisplay(product.CreatedAt)
};
}
}
Không còn magic. Tất cả là C# thuần.
7. So sánh nhanh
| Tiêu chí | AutoMapper | Mapperly |
|---|---|---|
| Thời điểm mapping | Runtime | Compile-time |
| Debug | Khó | Dễ |
| Performance | Trung bình | Tốt hơn |
| Độ rõ ràng | Config-based | Code-based |
8. Mapping ngược (form submit)
Ví dụ: tạo Product từ form:
public class ProductCreateViewModel
{
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
}
[Mapper]
public partial class ProductMapper
{
public partial Product ToEntity(ProductCreateViewModel model);
private DateTime MapCreatedAt()
=> DateTime.Now;
private bool MapIsActive()
=> true;
}
Không cần .ReverseMap(), chỉ cần define method rõ ràng.
9. Dùng trong Controller
public class ProductController : Controller
{
private readonly ProductMapper _mapper = new ProductMapper();
public ActionResult Index()
{
var products = _service.GetAll();
var result = products
.Select(p => _mapper.ToDto(p))
.ToList();
return View(result);
}
}
Không cần config global, không cần init.
10. Kết luận
AutoMapper vẫn là một tool tốt — đặc biệt với project nhỏ.
Nhưng với project cần:
- Dễ debug
- Performance tốt
- Code rõ ràng
Mapperly là một lựa chọn rất đáng cân nhắc.
Sau Part 1 + Part 2, bạn đã:
- Có project SDK-style sạch
- Mapping rõ ràng, compile-time
Và quan trọng nhất: codebase đã sẵn sàng cho các bước nâng cấp lớn hơn.
Tổng kết series
- Part 1: dọn nền project (SDK-style)
- Part 2: tối ưu mapping (Mapperly)
Hai bước nhỏ — nhưng impact rất lớn về lâu dài.
Nhận xét
Đăng nhận xét