مقدمه
از auto mapper ها برای نگاشت کردن (Mapping) دو شیء یا ایجاد آبجکتهای دارای دیتا از روی آبجکت دیگر استفاده
میکنیم.
یک نمونه
استفاده از این کتابخانه، در زمان نگاشت کردن DTO ها به شیء مدل است.
امروز دو مورد از این ابزار را با یکدیگر بررسی کرده و به سؤالات زیر پاسخ میدهیم:
-
آیا دو شیء باید از هم ارث برده باشند تا نگاشت صورت بگیرد؟
-
آیا بدون ارثبری و تنها با شبیه بودن نوع داده و نام ویژگی (Property)، همچنان نگاشت صورت میگیرد؟
-
ازلحاظ بهینه بودن کدامیک بهتر است؟
انواع کتابخانههای نگاشت
نام پکیج |
تعداد دانلود |
آخرین تاریخ آپدیت
تا زمان نوشته شدن این مطلب
|
حجم فایل |
توضیحات |
AutoMapper |
37,160,701 |
2019/04/26 |
335kb |
|
Mapster |
346,858 |
2019/04/13 |
232kb |
- سریعتر
- استفاده CPU بیشتر
|
TinyMapper |
139,155 |
2018/6/7 |
94kb |
از سال 2015 تاکنون فقط نسخه بتا منتشر کرده است. |
OoMapper |
8,456 |
2014/6/30 |
107kb |
-- |
EmitMapper |
174,707 |
2011/7/17 |
64kb |
تنها همین یک نسخه منتشرشده است. |
ValueInjecter |
898,512 |
2018/6/1 |
44kb |
-- |
در این قسمت به بررسی Mapster و Automapper میپردازیم.
Mapster
شیوه دریافت
برای دریافت دو راه داریم:
-
یکی از طریق package manager console که عبارت زیر را داخل آن تایپ میکنیم:
Install-package mapster
-
یکی از طریق آدرس زیر:
Tools > NuGet package manager > Manage NuGet packages for solution
و بعد عبارت Mapster را در تب browse صفحه بازشده، جستجو میکنیم.
تا زمان نوشتن این مطلب mapster تعداد 347 هزار بار در پروژههای مختلف دانلود و نصبشده است.
شیوه استفاده
شیوههای استفاده بهصورت زیر است:
-
در حالت ارثبری: طبق کد انجام میشود.
public class Person
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
}
public class LegalInformation : Person
{
public bool IsLegalAge
{
get { return Age > 18; }
}
}
static void SimpleMapping()
{
var person = new Person()
{
Firstname = "joe",
Lastname = "smith",
Age = 30
};
var personWithLegalInfo = person.Adapt<Person, LegalInformation>();
Console.WriteLine(string.Format("Firstname: {0}, Lastname: {1}, Age: {2}, is legal Age: {3}", personWithLegalInfo.Firstname, personWithLegalInfo.Lastname, personWithLegalInfo.Age, personWithLegalInfo.IsLegalAge));
}
-
در حالت ویژگیهای همنام:
-
در حالتی که هردو آبجکت از یک نوع کلاس باشند: برای clone کردن یک آبجکت بدون ارثبری
از ICloneable مناسب است.
-
آبجکت با نوع کلاسهای متفاوت با ویژگیهای هم نام و هم نوع:
جدای از نوع ویژگی، اگر کوچک و بزرگ بودن نام ویژگی را رعایت کرده باشیم، نگاشت صورت
میگیرد. اگر نام ویژگیها کاملاً مشابه بوده ولی نوع دادههای متفاوت داشته باشند، با
اکسپشن
مواجه میشویم و باید با استفاده از پیکرهبندی (Config) مناسب از بروز خطا جلوگیری کنیم.
-
با پیکرهبندی:
استفاده از Mapster با تعریف پیکرهبندی از دو راه صورت میگیرد:
-
استفاده از یک آبجکت TypeAdapterConfig که تنظیمات مناسب را در اختیار Mapster قرار
میدهد.
static void ConfigMapping()
{
var person = new Person()
{
Firstname = "joe",
Lastname = "smith",
Age = 30
};
var personWithLegalInfo = person.Adapt<person,>(
new TypeAdapterConfig()
.Default
.IgnoreMember((member, side) => (member.Name == "Age" && side == MemberSide.Source))
.EnableNonPublicMembers(true)
.MaxDepth(1)
.IgnoreNullValues(true)
.Config
);
Console.WriteLine(string.Format("Firstname: {0}, Lastname: {1}, Age: {2}, is legal Age: {3}", personWithLegalInfo.Firstname, personWithLegalInfo.Lastname, personWithLegalInfo.Age, personWithLegalInfo.IsLegalAge));
}
</person,>
-
دیگری استفاده از دو صفت (Attribute) زیر در کلاس مدل:
-
[AdaptIgnore]
همانطور که از نام صفت پیداست، در زمان نگاشت ویژگی موردنظر را نگاشت
نمیکند.
-
[(AdaptMember(name]:
این صفت یک String قبول میکند که نام ویژگی هدف در زمان نگاشت شدن
اشاره دارد. به عبارتی با
ویژگی هم نام خود نگاشت نمیشود و در کلاس هدف ویژگی با این عنوان را پیدا
میکند.
Auto mapper
شیوه دریافت
برای دریافت دو راه داریم:
-
یکی از طریق package manager console که عبارت زیر را در آن وارد میکنیم:
Install-package automapper
-
یکی از طریق آدرس زیر:
Tools > NuGet package manager > Manage NuGet packages for solution
و بعد عبارت automapper را در تب browse صفحه بازشده، جستجو میکنیم.
تا زمان نوشتن این مطلب automapper در حدود 37 میلیون بار در پروژههای مختلف دانلود و نصبشده
است.
شیوه استفاده
یک تفاوتی که در سینتکس و شیوه استفاده با Mapster دارد، این است که باید پیش از تبدیل و استفاده، تبدیلهایی که
میخواهید از آنها استفاده کنید را در اختیار mapster گذاشته و Mapper را initialize کرده باشید:
Mapper.Initialize(cfg => cfg.CreateMap<person,>());
همچنین توجه کنید که در هر context باید تنها یکبار این متد Mapper.Initialize را صدا بزنید؛ چراکه استاتیک بوده و
با بیش از یکبار صدازدن با اکسپشن مواجه میشوید.
اگر احتیاج به استفاده از پیکرهبندیهای متعدد دارید و باید چندین نوع کلاس را به یکدیگر نگاشت کنید،
میتوانید
خودتان
کلاس Mapper را initialize کرده و بهصورت زیر استفاده کنید:
var person = new Person()
{
Firstname = "joe",
Lastname = "smith",
Age = 30
};
var config = new MapperConfiguration(cfg =>
{
cfg.ShouldMapProperty = info => info.GetMethod.IsPublic;
cfg.CreateMap<person,>().MaxDepth(2)
.ForMember(destination => destination.Age, option => option.Condition(source => source.Age < 5)) //will be mapped if source age is less than 5
;
});
var myMapper = new Mapper(config);
var personWithLegalInfo = myMapper.DefaultContext.Mapper.Map<LegalInformation>(person);
Console.WriteLine(string.Format("Firstname: {0}, Lastname: {1}, Age: {2}, is legal Age: {3}", personWithLegalInfo.Firstname, personWithLegalInfo.Lastname, personWithLegalInfo.Age, personWithLegalInfo.IsLegalAge));
شیوههای استفاده بهصورت زیر است:
-
در حالت ارثبری: طبق روال انجام میشود.
static void SimpleMapping()
{
var person = new Person()
{
Firstname = "joe",
Lastname = "smith",
Age = 30
};
var personWithLegalInfo = person.Adapt<Person, LegalInformation>();
Console.WriteLine(string.Format("Firstname: {0}, Lastname: {1}, Age: {2}, is legal Age: {3}", personWithLegalInfo.Firstname, personWithLegalInfo.Lastname, personWithLegalInfo.Age, personWithLegalInfo.IsLegalAge));
}
-
در حالت ویژگیهای همنام:
-
در حالتی که هردو آبجکت از یک نوع کلاس باشند
: برای clone کردن یک آبجکت بدون ارثبری از ICloneable مناسب است.
-
آبجکت با نوع کلاسهای متفاوت با ویژگیهای هم نام و هم نوع:
جدای از نوع ویژگی، اگر کوچک و بزرگ بودن نام ویژگی را رعایت کرده باشیم، نگاشت صورت میگیرد.
اگر نام ویژگیها کاملاً مشابه بوده ولی نوع دادههای متفاوت داشته باشند با اکسپشن مواجه
میشویم
و باید با استفاده از پیکرهبندی مناسب از بروز خطا جلوگیری کنیم.
-
با پیکرهبندی:
AutoMapper از همان ابتدا به پیکرهبندی احتیاج دارد که در کد زیر برخی از موارد استفاده آن را
میبینیم. برای مثالهای بیشتر به لینک منبع مراجعه کنید.
static void ConfigMappingAutoMapper()
{
var person = new Person()
{
Firstname = "joe",
Lastname = "smith",
Age = 30
};
var config = new MapperConfiguration(cfg =>
{
cfg.ShouldMapProperty = info => info.GetMethod.IsPublic;
cfg.CreateMap<person,>().MaxDepth(2)
.ForMember(destination => destination.Age, option => option.Condition(source => source.Age < 5)) //will be mapped if source age is less than 5
;
});
var myMapper = new Mapper(config);
var personWithLegalInfo = myMapper.DefaultContext.Mapper.Map<LegalInformation>(person);
Console.WriteLine(string.Format("Firstname: {0}, Lastname: {1}, Age: {2}, is legal Age: {3}", personWithLegalInfo.Firstname, personWithLegalInfo.Lastname, personWithLegalInfo.Age, personWithLegalInfo.IsLegalAge));
}
مقایسه
-
AutoMapper برای استفاده به پیکرهبندی پیچیدهتری نیاز دارد.
-
گفتهشده که mapster تا 2.5 برابر سریعتر از automapper و هر Mapper شناختهشده دیگری است.
-
حجم فایل mapster، 232kb و حجم فایل autoMapper، 335kb است.
-
Automapper بهصورت پیشفرض فقط ویژگیهای پابلیک را نگاشت میکند و اگر بخواهیم ویژگی با سایر
accessor ها
را
نگاشت کند، باید در پیکرهبندی تنظیمات مناسب را انجام دهیم.
-
در مقایسه کارایی (Performance) برای نگاشت کردن یک نوع کلاس و در یک متد، Mapster تا 44% سرعت بالاتر دارد؛ اما
از
CPU به
مقدار بسیار کمی، بیشتر کار میکشد.
-
در مقایسه کارایی برای نگاشت کردن یک نوع کلاس و در یک متد، Automapper کندتر عمل میکند اما لود آن بر CPU به
مقدار بسیار اندک، کمتر است.
کارایی Mapster:
کارایی AutoMapper:
نتیجه
اگر احتیاج به نگاشت ساده و سبک دارید، بهتر است که از Mapster استفاده کنید.
اگر احتیاج به نگاشت پیچیده دارید که باید قوانین متعددی را در آن مدنظر داشته باشید، بهتر است که از AutoMapper
استفاده کنید.
منابع