فرض کنید کلاسهای مدل برنامه از سه کلاس مشتری، سفارشات مشتریها و اقلام هر سفارش تشکیل شدهاست:
در اینجا برای پیاده سازی خواص محاسباتی، از نکتهی مطرح شدهی در مطلب «نگاشت خواص محاسبه شده به کمک AutoMapper و DelegateDecompiler» استفاده شدهاست.
در ادامه میخواهیم اطلاعات حاصل از این کلاسها را با شرایط خاصی به ViewModelهای مشخصی جهت نمایش در برنامه نگاشت کنیم.
نمایش اطلاعات مشتریها
میخواهیم اطلاعات مشتریها را مطابق فرمت کلاس ذیل بازگشت دهیم:
با این شرایط که
- اگر Bio نال بود، بجای آن N/A نمایش داده شود.
- CustomerName از خاصیت محاسباتی FullName کلاس مشتری تامین گردد.
برای حل این مساله، نیاز است نگاشت زیر را تهیه کنیم:
AutoMapper برای جایگزین کردن خواص با مقدار نال، با یک مقدار مشخص، از متدی به نام NullSubstitute استفاده میکند. اما در این حالت خاص که قصد داریم از Project To استفاده کنیم، این روش پاسخ نمیدهد و محدودیتهایی دارد. به همین جهت از روش map from و بررسی مقدار خاصیت، استفاده شدهاست.
همچنین در اینجا مطابق نگاشت فوق، خاصیت CustomerName از خاصیت FullName کلاس مشتری دریافت میشود.
کوئری نهایی استفاده کنندهی از این اطلاعات به شکل زیر خواهد بود:
در اینجا از متدهای Project To و همچنین Decompile استفاده شدهاست (جهت پردازش خاصیت محاسباتی).
نمایش اطلاعات سفارشهای مشتریها
در ادامه قصد داریم اطلاعات سفارشها را با فرمت ViewModel ذیل نمایش دهیم:
با این شرایط که
- CustomerName از خاصیت محاسباتی FullName کلاس مشتری تامین گردد.
- خاصیت OrderNumber آن از خاصیت OrderNo تهیه گردد.
به همین جهت کار را با تهیهی نگاشت ذیل ادامه میدهیم:
بر این اساس کوئری مورد استفاده نیز به نحو ذیل تشکیل میشود:
در اینجا چون از خاصیت OrderItems کلاس ViewModel صرفنظر نشدهاست، اطلاعات آن نیز به همراه viewOrders موجود است. یعنی میتوان چنین کوئری را نیز جهت نمایش اطلاعات تو در توی اقلام هر سفارش نیز نوشت:
اگر میخواهید OrderItems به صورت خودکار واکشی نشود، نیاز است در نگاشت تهیه شده، توسط متد Ignore از آن صرفنظر کنید:
نمایش اطلاعات یک سفارش، با فرمتی خاص
تا اینجا نگاشتهای انجام شده بر روی لیستی از اشیاء صورت گرفتند. در ادامه میخواهیم اولین سفارش ثبت شده را با فرمت ذیل نمایش دهیم:
به همین منظور ابتدا نگاشت ذیل را تهیه میکنیم:
در اینجا ساعت و دقیقهی خرید، از خاصیت PurchaseDate استخراج شدهاند. همچنین CustomerName نیز از خاصیت FullName کلاس مشتری دریافت گردیدهاست.
پس از این تنظیمات، کوئری نهایی به شکل ذیل خواهد بود:
فرمت کردن سفارشی اطلاعات در حین نگاشتها
در مورد فرمت کنندههای سفارشی و تبدیلگرها پیشتر بحث کردهایم. اما اغلب آنها را در حالت خاص LINQ to Entities نمیتوان بکار برد، زیرا قابلیت تبدیل به SQL را ندارند. برای مثال فرض کنید میخواهیم خاصیت ShipToHomeAddress کلاس Order را به خاصیت ShipHome کلاس ذیل نگاشت کنیم:
با این شرط که اگر مقدار آن True بود، Yes را نمایش دهد. با توجه به ساختار مدنظر، نگاشت ذیل را میتوان تهیه کرد که در آن فرمت کردن سفارشی، به متد MapFrom واگذار شدهاست:
با این کوئری جهت استفادهی از این تنظیمات:
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
public class Customer { public int Id { set; get; } public string FirstName { get; set; } public string LastName { get; set; } public string Bio { get; set; } public virtual ICollection<Order> Orders { get; set; } [Computed] [NotMapped] public string FullName { get { return FirstName + ' ' + LastName; } } } public class Order { public int Id { set; get; } public string OrderNo { get; set; } public DateTime PurchaseDate { get; set; } public bool ShipToHomeAddress { get; set; } public virtual ICollection<OrderItem> OrderItems { get; set; } [ForeignKey("CustomerId")] public virtual Customer Customer { get; set; } public int CustomerId { get; set; } [Computed] [NotMapped] public decimal Total { get { return OrderItems.Sum(x => x.TotalPrice); } } } public class OrderItem { public int Id { get; set; } public decimal Price { get; set; } public string Name { get; set; } public int Quantity { get; set; } [ForeignKey("OrderId")] public virtual Order Order { get; set; } public int OrderId { get; set; } [Computed] [NotMapped] public decimal TotalPrice { get { return Price * Quantity; } } }
در ادامه میخواهیم اطلاعات حاصل از این کلاسها را با شرایط خاصی به ViewModelهای مشخصی جهت نمایش در برنامه نگاشت کنیم.
نمایش اطلاعات مشتریها
میخواهیم اطلاعات مشتریها را مطابق فرمت کلاس ذیل بازگشت دهیم:
public class CustomerViewModel { public string Bio { get; set; } public string CustomerName { get; set; } }
- اگر Bio نال بود، بجای آن N/A نمایش داده شود.
- CustomerName از خاصیت محاسباتی FullName کلاس مشتری تامین گردد.
برای حل این مساله، نیاز است نگاشت زیر را تهیه کنیم:
this.CreateMap<Customer, CustomerViewModel>() .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(entity => entity.FullName)) .ForMember(dest => dest.Bio, opt => opt.MapFrom(entity => entity.Bio ?? "N/A"));
همچنین در اینجا مطابق نگاشت فوق، خاصیت CustomerName از خاصیت FullName کلاس مشتری دریافت میشود.
کوئری نهایی استفاده کنندهی از این اطلاعات به شکل زیر خواهد بود:
using (var context = new MyContext()) { var viewCustomers = context.Customers .Project() .To<CustomerViewModel>() .Decompile() .ToList(); // don't use // var viewCustomers = Mapper.Map<IEnumerable<Customer>, IEnumerable<CustomerViewModel>>(dbCustomers); foreach (var customer in viewCustomers) { Console.WriteLine("{0} - {1}", customer.CustomerName, customer.Bio); } }
نمایش اطلاعات سفارشهای مشتریها
در ادامه قصد داریم اطلاعات سفارشها را با فرمت ViewModel ذیل نمایش دهیم:
public class OrderViewModel { public string CustomerName { get; set; } public decimal Total { get; set; } public string OrderNumber { get; set; } public IEnumerable<OrderItemsViewModel> OrderItems { get; set; } } public class OrderItemsViewModel { public string Name { get; set; } public int Quantity { get; set; } public decimal Price { get; set; } }
- CustomerName از خاصیت محاسباتی FullName کلاس مشتری تامین گردد.
- خاصیت OrderNumber آن از خاصیت OrderNo تهیه گردد.
به همین جهت کار را با تهیهی نگاشت ذیل ادامه میدهیم:
this.CreateMap<Order, OrderViewModel>() .ForMember(dest => dest.OrderNumber, opt => opt.MapFrom(src => src.OrderNo)) .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.FullName));
using (var context = new MyContext()) { var viewOrders = context.Orders .Project() .To<OrderViewModel>() .Decompile() .ToList(); // don't use // var viewOrders = Mapper.Map<IEnumerable<Order>, IEnumerable<OrderViewModel>>(dbOrders); foreach (var order in viewOrders) { Console.WriteLine("{0} - {1} - {2}", order.OrderNumber, order.CustomerName, order.Total); } }
using (var context = new MyContext()) { var viewOrders = context.Orders .Project() .To<OrderViewModel>() .Decompile() .ToList(); // don't use // var viewOrders = Mapper.Map<IEnumerable<Order>, IEnumerable<OrderViewModel>>(dbOrders); foreach (var order in viewOrders) { Console.WriteLine("{0} - {1} - {2}", order.OrderNumber, order.CustomerName, order.Total); foreach (var item in order.OrderItems) { Console.WriteLine("({0}) {1} - {2}", item.Quantity, item.Name, item.Price); } } }
this.CreateMap<Order, OrderViewModel>() .ForMember(dest => dest.OrderNumber, opt => opt.MapFrom(src => src.OrderNo)) .ForMember(dest => dest.OrderItems, opt => opt.Ignore()) .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.FullName));
نمایش اطلاعات یک سفارش، با فرمتی خاص
تا اینجا نگاشتهای انجام شده بر روی لیستی از اشیاء صورت گرفتند. در ادامه میخواهیم اولین سفارش ثبت شده را با فرمت ذیل نمایش دهیم:
public class OrderDateViewModel { public int PurchaseHour { get; set; } public int PurchaseMinute { get; set; } public string CustomerName { get; set; } }
this.CreateMap<Order, OrderDateViewModel>() .ForMember(dest => dest.PurchaseHour, opt => opt.MapFrom(src => src.PurchaseDate.Hour)) .ForMember(dest => dest.PurchaseMinute, opt => opt.MapFrom(src => src.PurchaseDate.Minute)) .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.FullName));
پس از این تنظیمات، کوئری نهایی به شکل ذیل خواهد بود:
using (var context = new MyContext()) { var viewOrder = context.Orders .Project() .To<OrderDateViewModel>() .Decompile() .FirstOrDefault(); // don't use // var viewOrder = Mapper.Map<Order, OrderDateViewModel>(dbOrder); if (viewOrder != null) { Console.WriteLine("{0}, {1}:{2}", viewOrder.CustomerName, viewOrder.PurchaseHour, viewOrder.PurchaseMinute); } }
فرمت کردن سفارشی اطلاعات در حین نگاشتها
در مورد فرمت کنندههای سفارشی و تبدیلگرها پیشتر بحث کردهایم. اما اغلب آنها را در حالت خاص LINQ to Entities نمیتوان بکار برد، زیرا قابلیت تبدیل به SQL را ندارند. برای مثال فرض کنید میخواهیم خاصیت ShipToHomeAddress کلاس Order را به خاصیت ShipHome کلاس ذیل نگاشت کنیم:
public class OrderShipViewModel { public string ShipHome { get; set; } public string CustomerName { get; set; } }
this.CreateMap<Order, OrderShipViewModel>() .ForMember(dest => dest.ShipHome, opt => opt.MapFrom(src=>src.ShipToHomeAddress? "Yes": "No")) .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.FullName));
using (var context = new MyContext()) { var viewOrders = context.Orders .Project() .To<OrderShipViewModel>() .Decompile() .ToList(); // don't use // var viewOrders = Mapper.Map<IEnumerable<Order>, IEnumerable<OrderShipViewModel>>(dbOrders); foreach (var order in viewOrders) { Console.WriteLine("{0} - {1}", order.CustomerName, order.ShipHome); } }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.