2013-12-13 2 views
1

Спина от моего ранее вопроса ->How to map .NET function call to property automatically?Как рекурсивно отображать объект для просмотра модели с вызовом функции Automapper?

У меня есть несколько связанных объектов сущностей, которые я хочу карту для просмотра моделей, и каждое отображение в модель представления необходимо вызвать функцию для отображения.

Сложная часть состоит в том, что функция entity возвращает другой объект, который необходимо сопоставить с его моделью просмотра. Поток выглядит примерно так.

CartEntity > CartViewModel > CartEntity.GetCartItems() > CartViewModel.CartItems > CartEntity.GetCartItems().GetItem() > CartViewModel.CartItems.Item > CartEntity.GetCartItems().GetItem().GetProduct() > CartViewModel.CartItems.Item.Product

Вот классы, в которых я пытаюсь рекурсивно построить вложенный набор, который заполняется каждым объектом.

Сущность:

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;} 
} 

Вид модель:

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;} 
} 
+0

Я рекомендовал бы, чтобы ваши модели надевают 't делать вызовы в базе данных ... если бы объекты имели обычные свойства, все это бы просто работало –

+0

Отключение вышеуказанного комментария, если вы привязаны к вызову базы данных непосредственно из своих моделей, вы также можете использовать ленивый загруженный, только свойство вместо общедоступных методов, чтобы сделать сопоставление немного проще. Вот пример (извините, что это в одной строке): 'private Item _product; public Item Product {get {return _product ?? (_product = Repository.Get (ProductId)); }} ' – valverij

+0

Я использую Redis и следуя шаблону, используемому примерами ServiceStack. Не знаете, как реорганизовать без введения значительного количества нового кода. https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/Examples/BestPractice/BlogPostBestPractice.cs – user3092978

ответ

3

Если вы готовы изменить свои модели немного, бег AutoMapper должен быть довольно легко (если нет, перейдите к нижнему).

Во-первых, поскольку ваши комментарии показывают, что вам нужно позвонить в свои репозитории из своих моделей, я бы предложил изменить их на свойства только для чтения. Например:

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); 
    } 
} 

Кроме того, если вы не хотите попасть к базе данных каждый раз, когда вы под названием .Item, то вы можете добавить ленивый загруженным поле подкладочный:

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); 
    } 
} 

Оттуда , вы можете настроить AutoMapper так:

// 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); 

Если вы не хотите, чтобы изменить свои модели, чтобы использовать свойство (или, если вы не можете), то вы можете добавить использование т он ForMember конфигурации на картах, чтобы указать, что нужно сделать для каждого элемента:

Mapper.CreateMap<CartItem, CartItemViewModel>() 
     .ForMember(target => target.Item, x => x.ResolveUsing(source => source.GetItem())); 
+0

Я попытался переключить свою модель на ленивую версию, но я получаю ошибка при попытке сохранить CartItem, поскольку свойство Item вызывает ошибку. Я считаю, что это связано с тем, что репозиторий был нулевым. Возможно ли, что он скоро не вводится? Ошибка: ссылка на объект не установлена ​​в экземпляр объекта. + \t \t Пункт \t 'this.Item' бросил исключение из типа 'System.NullReferenceException ' – user3092978

+0

Если это репозиторий, просто убедитесь, что ваш репозиторий создан при создании объекта (либо в определении, либо в конструкторе). Если это не так, убедитесь, что ваш «ItemID» определен до того, как вы вызываете '.Item'. Если у вас по-прежнему возникают проблемы, вы можете попытаться сохранить методы и использовать конфигурацию AutoMapper 'ForMember', чтобы перейти оттуда вместо этого. – valverij

3

Вам не нужно делать ничего сложного, AutoMapper поддерживает это:

// 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); 
Смежные вопросы