در ادامه قسمت قبلی به برسی ویژگیهای پیشرفتهی AutoMapper میپردازیم...
Custom type converters
همانطور که از اسمش مشخصه، زمانی کاربرد داره که نوع عضو یا اعضای یک شی در مبداء، با معادلشون در مقصد یکی نیستند. مثلا فرض کنید نوع Bool در مبداء رو میخواهیم به نوع String در مقصد نگاشت کنیم؛ همون Yes و No معروف بجای True یا False .
کلاسهای زیر رو در نظر بگیرید:
طبق مستندات AutoMapper اگه بخواهیم این دو رو نگاشت کنیم Exception میده چون AutoMapper نمیدونه چطوری باید مثلا Int رو به String تبدیل کنه؛ برای همین ما باید به AutoMapper بگیم چطور این تبدیل نوع رو انجام بده.
نکته: در تستی که من انجام دادم، AutoMapper تبدیل نوعهای ابتدایی رو خودش انجام میده؛ مثلا همین تبدیل Int به String رو!
یکی از روشهای مهیا کردن تبدیل کنندهی نوع، پیاده سازی اینترفیس ITypeConverter<TSource, TDestination> هست. تقریبا مثل کاری که در WPF و SL با پیاده سازی اینترفیس IValueConverter انجام میدادیم.
من برای تست از همون تبدیل نوع Bool به String استفاده میکنم و البته بخاطر ساده بودن دیگه Model ها رو نمینویسم.
ابتدا تعریف کلاس تبدیل کنندهی نوع:
و نحوه استفاده:
خروجی به شکل زیر میشه.
فرض کنید داخل RawData تمامی اعضای شی مبداء رو به صورت Comma Delimited ذخیره کنیم. برای این کار میتونیم از Value Resolver استفاده کنیم.
یک روش برای این کار ارث بری از کلاس Abstract ی بنام ValueResolver<TSource, TDestination> هست.
و نحوه استفاده
و خروجی به شکل زیر میشه
نکته: توجه کنید این فقط یک مثال بود و این کار رو با روشهای دیگه هم میشه انجام داد مثلا MapFrom و...
نکته: میدان دید Value Resolverها سراسری نیست و باید به ازای هر نگاشتی اونو معرفی کنیم.
Custom Value Formatters
فرض کنید تاریخ رو در بانک، به صورت میلادی ذخیره کردهاید و میخواهید سمت View به صورت شمسی نمایش بدید. بنابراین در مبدا ویژگی بنام MiladiDate از نوع DateTime دارید و در مقصد ویژگی بنام ShamsiDate از نوع String. هنگام نگاشت، AutoMapper به صورت پیش فرض ToString رو فراخونی میکنه که بدرد ما نمیخوره و...
برای این کار میشه از Value Formatter استفاده کرد با پیاده سازی اینترفیس IValueFormatter.
نحوه استفاده
Custom type converters
همانطور که از اسمش مشخصه، زمانی کاربرد داره که نوع عضو یا اعضای یک شی در مبداء، با معادلشون در مقصد یکی نیستند. مثلا فرض کنید نوع Bool در مبداء رو میخواهیم به نوع String در مقصد نگاشت کنیم؛ همون Yes و No معروف بجای True یا False .
کلاسهای زیر رو در نظر بگیرید:
public class Source { public string Value1 { get; set; } public string Value2 { get; set; } public string Value3 { get; set; } } public class Destination { public int Value1 { get; set; } public DateTime Value2 { get; set; } public Type Value3 { get; set; } }
نکته: در تستی که من انجام دادم، AutoMapper تبدیل نوعهای ابتدایی رو خودش انجام میده؛ مثلا همین تبدیل Int به String رو!
یکی از روشهای مهیا کردن تبدیل کنندهی نوع، پیاده سازی اینترفیس ITypeConverter<TSource, TDestination> هست. تقریبا مثل کاری که در WPF و SL با پیاده سازی اینترفیس IValueConverter انجام میدادیم.
من برای تست از همون تبدیل نوع Bool به String استفاده میکنم و البته بخاطر ساده بودن دیگه Model ها رو نمینویسم.
ابتدا تعریف کلاس تبدیل کنندهی نوع:
public class BooltoStringTypeConvertor : ITypeConverter<bool, string> { public string Convert(ResolutionContext context) { return (bool)context.SourceValue ? "Yes" : "No"; } }
Mapper.CreateMap<bool,string>().ConvertUsing<BooltoStringTypeConvertor>(); Mapper.CreateMap<Product, ProductDto>(); Mapper.AssertConfigurationIsValid(); var product = new Product { Id = 1,Name ="PC" ,InStock = true }; var productDto = Mapper.Map<Product, ProductDto>(product);
نکته: TypeConvertorها میدان دیدشون سراسریه و نیازی نیست به ازای هر نگاشتی اونو به AutoMapper معرفی کنیم Global Scope.
Custom value resolvers
کلاسهای زیر رو در نظر بگیرید
public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } public class PersonDTO { public int Id { get; set; } public string RawData { get; set; } }
یک روش برای این کار ارث بری از کلاس Abstract ی بنام ValueResolver<TSource, TDestination> هست.
public class CommaDelimetedResolver:ValueResolver<Person,string> { protected override string ResolveCore(Person source) { return string.Join(",", source.Id, source.FirstName, source.LastName); } }
Mapper.CreateMap<Person, PersonDTO>().ForMember( des => des.RawData, op => op.ResolveUsing<CommaDelimetedResolver>()); var person = new Person { Id = 1, FirstName = "Mohammad", LastName = "Saheb", }; var personDTO = Mapper.Map<Person, PersonDTO>(person);
نکته: توجه کنید این فقط یک مثال بود و این کار رو با روشهای دیگه هم میشه انجام داد مثلا MapFrom و...
نکته: میدان دید Value Resolverها سراسری نیست و باید به ازای هر نگاشتی اونو معرفی کنیم.
Custom Value Formatters
فرض کنید تاریخ رو در بانک، به صورت میلادی ذخیره کردهاید و میخواهید سمت View به صورت شمسی نمایش بدید. بنابراین در مبدا ویژگی بنام MiladiDate از نوع DateTime دارید و در مقصد ویژگی بنام ShamsiDate از نوع String. هنگام نگاشت، AutoMapper به صورت پیش فرض ToString رو فراخونی میکنه که بدرد ما نمیخوره و...
برای این کار میشه از Value Formatter استفاده کرد با پیاده سازی اینترفیس IValueFormatter.
public class ShamsiFormatter:IValueFormatter { public string FormatValue(ResolutionContext context) { return ToShamsi(context.SourceValue.ToString()); } }
Mapper.CreateMap<Person, PersonDTO>().ForMember( des => des.ShamsiDate, op => op.AddFormatter<ShamsiFormatter>());