Spin off of my earlier question -> How to map .NET function call to property automatically?
I have some related entity objects that I want to map to view models, and each mapping to a view model needs to call a function for the mapping.
The tricky part is that the entity function returns another entity which needs to be mapped to it's view model. The flow is somewhat like this.
CartEntity > CartViewModel > CartEntity.GetCartItems() >
CartViewModel.CartItems > CartEntity.GetCartItems().GetItem() >
CartViewModel.CartItems.Item >
CartEntity.GetCartItems().GetItem().GetProduct() >
CartViewModel.CartItems.Item.Product
Here are the classes, where I am trying to recursively build a nested set that is populated with each object.
Entities:
public class CartEntity {
public long Id {get; set;}
public List<CartItem> GetCartItems() {
return Repository.GetAll<CartItem>();
}
}
public class CartItem {
public long Id {get; set;}
public long ItemId {get ; set;}
public Item GetItem() {
return Repository.Get<Item>(ItemId);
}
}
public class Item {
public long Id {get; set;}
public long ProductId {get; set;}
public Item GetProduct() {
return Repository.Get<Product>(ProductId);
}
}
public class Product {
public long Id {get; set;}
}
View Models:
public class CartViewModel {
public long Id {get; set;}
public List<CartItemViewModel> {get; set; }
}
public class CartItemViewModel {
public long Id {get; set;}
public ItemViewModel Item {get; set; }
}
public class ItemViewModel {
public long Id {get; set;}
public ProductViewModel Product {get; set; }
}
public class ProductViewModel {
public long Id {get; set;}
}
If you're willing to change your models a bit, running AutoMapper should be pretty easy (if not, skip down to the bottom).
First, since your comments indicate that you need to call your repositories from your models, I'd suggest changing them to read-only properties. For example:
public class CartItem {
public long Id {get; set;}
public long ItemId {get ; set;}
public Item Item {
get {
return GetItem();
}
}
private Item GetItem() {
return Repository.Get<Item>(ItemId);
}
}
Also, if you didn't want to hit the database every time you called .Item
, then you could add a lazy-loaded backing field:
public class CartItem {
private Item _item;
public long Id {get; set;}
public long ItemId {get ; set;}
public Item Item {
get {
return _item ?? (_item = GetItem());
}
}
private Item GetItem() {
return Repository.Get<Item>(ItemId);
}
}
From there, you can set up AutoMapper like this:
// one-time, static configuration:
Mapper.CreateMap<CartEntity, CartViewModel>();
Mapper.CreateMap<CartItem, CartItemViewModel>();
Mapper.CreateMap<Item, ItemViewModel>();
Mapper.CreateMap<Product, ProductViewModel>();
// running the mapper:
var cartViewModel = Mapper.Map<CartViewModel>(cart);
If you don't want to change your models to use properties (or if you can't), then you can add use the ForMember
configuration on your maps to specify what to do for each item:
Mapper.CreateMap<CartItem, CartItemViewModel>()
.ForMember(target => target.Item, x => x.ResolveUsing(source => source.GetItem()));
You don't need to do anything complicated, AutoMapper supports this:
// one time config
Mapper.CreateMap<CartEntity, CartViewModel>();
Mapper.CreateMap<CartItem, CartItemViewModel>();
Mapper.CreateMap<Item, ItemViewModel>();
Mapper.CreateMap<Product, ProductViewModel>();
Mapper.AssertConfigurationIsValid();
// whenever you map
CartEntity cart = // whatever
CartViewModel vm = Mapper.Map<CartViewModel>(cart);