عنوان:

‫Facet؛ یک source generator جایگزین AutoMapper


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۲/۰۹ ۰۸:۴۲
آدرس: www.dntips.ir

مقدمه

مدیریت داده‌ها بین لایه‌های مختلف یک برنامه، از جمله لایه دامنه (domain)، لایه انتقال داده (data transfer) و لایه نمایش (presentation)، یک وظیفه رایج و در عین حال تکراری است. توسعه‌دهندگان اغلب با حجم زیادی از کلاس‌های انتقال داده (DTOs)، منطق نگاشت (mapping) بین این کلاس‌ها و مدل‌های دامنه، و پیکربندی‌های مربوط به ابزارهای نگاشت خودکار مانند اتومپر (AutoMapper) مواجه می‌شوند. این فرایند دستی نه تنها زمان‌بر است، بلکه می‌تواند منجر به خطاهای انسانی و افزایش حجم کد بویلرپلیت (boilerplate) در پروژه‌ها شود.
در این مقاله، به بررسی یک رویکرد نوین برای حل این چالش می‌پردازیم: استفاده از تولیدکننده کد منبع (source generator) به نام فست (Facet). فست با تولید خودکار DTOها و منطق نگاشت مربوطه در زمان کامپایل، به توسعه‌دهندگان کمک می‌کند تا کد کمتری بنویسند، از سربار زمان اجرا (runtime overhead) جلوگیری کنند و از مزایای تایپ قوی (#C) و آگاهی از تهی‌پذیری (nullable awareness) بهره‌مند شوند.

مفهوم فست (Facet) و نحوه عملکرد آن

فست (Facet) به عنوان یک تولیدکننده کد منبع #C، با هدف تسهیل فرایند ایجاد و مدیریت DTOها در برنامه‌های دات‌نت طراحی شده است. ایده اصلی فست، تعریف نماهای متمرکز (focused views) از یک مدل غنی‌تر در زمان کامپایل است. به جای نوشتن دستی DTOهای جداگانه، کلاس‌های نگاشت و عبارات لامبدا (lambda expressions) برای طرح‌ریزی (projection)، توسعه‌دهندگان می‌توانند با استفاده از ویژگی‌های (attributes) فست، مشخص کنند که کدام اعضای مدل دامنه باید در DTO تولید شده گنجانده یا حذف شوند.
هنگامی که پروژه دات‌نت کامپایل می‌شود، تولیدکننده کد منبع فست، تحلیل‌های لازم را بر روی مدل‌های دامنه انجام داده و بر اساس تعاریف موجود در ویژگی‌های [Facet], کلاس‌های جزئی (partial classes) یا رکوردها (records) را به همراه سازنده‌های (constructors) قوی تایپ شده و عبارات طرح‌ریزی LINQ (Expression<Func<TSource,TTarget>>) تولید می‌کند. این فرایند به طور کامل در زمان کامپایل انجام می‌شود و هیچ سربار عملکردی در زمان اجرا به برنامه تحمیل نمی‌کند.

ویژگی‌های کلیدی فست (Facet)

تولید خودکار DTO: با استفاده از ویژگی [Facet] بر روی یک کلاس دامنه، می‌توان DTOهای سفارشی را با انتخاب یا حذف خواص مورد نظر تولید کرد.
public class Person
{
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
}

[Facet(typeof(Person), exclude: nameof(Person.Email))]
public partial class PersonDto;
کد بالا یک کلاس جزئی به نام PersonDto تولید می‌کند که شامل خواص Name و Age از کلاس Person است و خاصیت Email را حذف می‌کند. همچنین، یک سازنده برای نگاشت از Person به PersonDto و یک عبارت Projection از نوع Expression<Func<Person,PersonDto>> تولید می‌شود.

فست از نوع رکورد (Record-style Facets): فست امکان تولید DTOها به صورت رکورد (#C 9.0) را نیز فراهم می‌کند که برای ساختارهای داده‌ای غیرقابل تغییر (immutable) مناسب هستند.
[Facet(typeof(Person), Kind = FacetKind.Record)]
public partial record PersonRecord;

نگاشت سفارشی (Custom Mapping): برای سناریوهای پیچیده‌تر که نیاز به انجام عملیات اضافی در هنگام نگاشت وجود دارد، فست از طریق رابط IFacetMapConfiguration<TSource,TTarget> امکان تعریف منطق نگاشت سفارشی را فراهم می‌کند.
public class UserMapConfig : IFacetMapConfiguration<User, UserDto>
{
    public static void Map(User source, UserDto target)
    {
        target.FullName = $"{source.FirstName} {source.LastName}";
    }
}

[Facet(typeof(User), Configuration = typeof(UserMapConfig))]
public partial class UserDto;
در این مثال، کلاس UserMapConfig نحوه نگاشت از کلاس User به UserDto را تعریف می‌کند و فیلد FullName را با ترکیب FirstName و LastName پر می‌کند.

ادغام با LINQ و EF Core: بسته الحاقی Facet.Extensions، متدهای توسعه‌ای (extension methods) را برای ساده‌سازی استفاده از فست با LINQ و EF Core ارائه می‌دهد.
using Facet.Extensions;

var person = new Person { Name = "Alice", Email = "alice@example.com", Age = 30 };
var dto = person.ToFacet<Person, PersonDto>(); // نگاشت یک شیء

var dtos = dbContext.People
    .SelectFacet<Person, PersonDto>() // طرح‌ریزی IQueryable
    .ToListAsync();
متدهای ToFacet, SelectFacets و SelectFacet فرایند نگاشت را به صورت یکپارچه در جریان LINQ و EF Core ادغام می‌کنند.

مزایای استفاده از فست (Facet)

  • کاهش کد بویلرپلیت: فست با تولید خودکار DTOها و منطق نگاشت، نیاز به نوشتن کد تکراری را به میزان قابل توجهی کاهش می‌دهد.
  • بهبود عملکرد: از آنجایی که تمام منطق نگاشت در زمان کامپایل تولید می‌شود، هیچ سربار عملکردی در زمان اجرا وجود ندارد. این امر به ویژه در برنامه‌های با حجم بالای داده و عملیات نگاشت مکرر حائز اهمیت است.
  • افزایش ایمنی نوع: فست از تایپ قوی #C بهره می‌برد و از بروز خطاهای مربوط به نگاشت در زمان اجرا جلوگیری می‌کند. همچنین، از ویژگی‌های آگاهی از تهی‌پذیری #C برای اطمینان از مدیریت صحیح مقادیر تهی (null) استفاده می‌کند.
  • رعایت اصل DRY (Don't Repeat Yourself): با تعریف نگاشت‌ها در یک مکان و تولید خودکار کد مربوطه، فست به حفظ اصل عدم تکرار در کد کمک می‌کند.
  • شروع سریع: نصب و استفاده از فست بسیار آسان است و به سرعت می‌توان از مزایای آن در پروژه‌ها بهره‌مند شد.

نتیجه‌گیری

فست (Facet) یک ابزار قدرتمند و کارآمد برای توسعه‌دهندگان دات‌نت است که به دنبال راهی برای ساده‌سازی و بهینه‌سازی فرایند مدیریت DTOها و نگاشت داده‌ها هستند. با استفاده از تولید کد منبع در زمان کامپایل، فست نه تنها حجم کد بویلرپلیت را کاهش می‌دهد، بلکه عملکرد برنامه را بهبود بخشیده و ایمنی نوع را افزایش می‌دهد. ادغام یکپارچه آن با LINQ و EF Core، فست را به یک انتخاب جذاب برای پروژه‌های مدرن دات‌نت تبدیل می‌کند. توسعه‌دهندگان می‌توانند با نصب بسته NuGet فست و استفاده از ویژگی‌های آن، تجربه توسعه‌ای سریع‌تر، تمیزتر و کارآمدتر را تجربه کنند.


مشاهده مطلب اصلی