How to flatten/unflatten collections?

Jun 11, 2011 at 4:12 PM

Please see example below...
My question is do I need to flatten/unflatten in a loop or does VI do it automatically?

Thanks
z...

public class ModelCollectionToViewModelCollection
{
        public interface IIdentifiable<T>
        {
            T Id { get; set; }
        }
		
        public abstract class Enitity<T> : IIdentifiable<T>
        {
            public virtual T Id { get; set; }
        }
		
        // root entity (references 1 part)
        public class Item : Entity<int>
        {
            public string Name { get; set; }
            public double? Cost { get; set; }
            public Part Part { get; set; }
        }
		
        // child entity
        public class Part : Entity<string>
        {
            public string Name { get; set; }
        }
		
        // flat vm easiest...
        public class ViewModel
        {
            public int ItemId { get; set; }
            public string ItemName { get; set; }
            public double? ItemCost { get; set; }
            public string PartId { get; set; }
            public string PartName { get; set; }
        }

        public void Test()
        {
            // create some entities
            var p1 = new Part { Id = 100, Name = "Part 100" };
            var p2 = new Part { Id = 200, Name = "Part 200" };
            var i1 = new Item { Id = 1, Name = "Item 1", Cost = 50.00m, Part = p1 };
            var i2 = new Item { Id = 2, Name = "Item 2", Cost = 75.00m, Part = p1 };
            var i2 = new Item { Id = 2, Name = "Item 2", Part = p2 };

            // add all item entities to collection (simulate collection retuned by db/repository retrieveal)
            var models = new List<Item> { i1, i2, i3 };

            // create a list of view models
            var viewModels = new List<ViewModel>();

            // flat map models to viewModels <-- does nto work...Do I have to do this in a loop?
            viewModels.InjectFrom<FlatLoopValueInjection>(models);
        }
}

 

Coordinator
Jun 11, 2011 at 7:31 PM

you do a foreach,

if you want automatic stuff look at the automapper simulation http://valueinjecter.codeplex.com/wikipage?title=Automapper%20Simulation&referringTitle=Home

Jun 11, 2011 at 9:08 PM

Hi

Thanks for such a quick reply....
I looked at the Automapper simulation, and it seems a bit "heavy" for my needs. I also needed to "hide" the implementation of mappings to my app so after looking over your code base, ProDinner, and some other projects I took following approach...

Can you advise if there is anything wrong with this approach?

 

    public interface IMapper<TEntity, TModel>
        where TEntity : class, new()
        where TModel : new()
    {
        TModel MapToModel(TEntity entity);
        TEntity MapToEntity(TModel model);

        IEnumerable<TModel> MapToModel(IEnumerable<TEntity> entities);
        IEnumerable<TEntity> MapToEntity(IEnumerable<TModel> models);

        TModel RemapModel(TModel model);
    }


    public abstract class BaseMapper<TEntity, TModel> : IMapper<TEntity, TModel>
        where TEntity : class, new()
        where TModel : new()
    {
        public BaseMapper()
        {
            //RFU
        }

        public abstract TModel MapToModel(TEntity entity);
        public abstract TEntity MapToEntity(TModel model);

        public virtual IEnumerable<TModel> MapToModel(IEnumerable<TEntity> entities)
        {
            return entities.Select(entity => MapToModel(entity)).AsEnumerable();
        }

        public virtual IEnumerable<TEntity> MapToEntity(IEnumerable<TModel> models)
        {
            return models.Select(model => MapToEntity(model)).AsEnumerable();
        }

        public virtual TModel RemapModel(TModel model)
        {
            return MapToModel(MapToEntity(model));
        }
    }


    public class ItemMapper : BaseMapper<Item, ItemViewModel>
    {
        private readonly ValueInjecter _injecter;

        public ItemMapper()
        {
            _injecter = new ValueInjecter();
        }

        public override ItemViewModel MapToModel(Item entity)
        {
            var viewModel = new ItemViewModel();
            _injecter.Inject<FlatLoopValueInjection>(viewModel, entity);
            return viewModel;
        }

        public override Item MapToEntity(ItemViewModel model)
        {
            var entity = new Item();
            _injecter.Inject<UnflatLoopValueInjection>(entity, model);
            return entity;
        }

    }


    // then in my controller
     public class ItemController : BaseController
    {
        private readonly ItemMapper _mapper;
        private readonly ItemService _itemService;

        //IoC will instantiate via constructor injection..
        public ItemController(ItemMapper mapper, ItemService itemService)
        {
            _mapper = mapper;
            _itemService = itemService;
        }

        public ActionResult Index()
        {
            var items = _itemService.GetAllItems();
            var viewModel = _mapper.MapToModel(items); //IEnumerable<Items> is mapped to IEnumerable<ItemViewModel>

            return View(viewModel);
        }

        public ActionResult Create()
        {
            var item = new Item() { Part = new Part() };
            var viewModel = _mapper.MapToModel(item);  //Items is mapped to ItemViewModel
            return View(viewModel);
        }


    }

Coordinator
Jun 12, 2011 at 7:13 AM

looks kinda ok, it looks like you always need to map collections, also I see in ItemMapper you map single object but in BaseMapper collections, so I don't really get the connection between them. does it work ?

Jun 15, 2011 at 3:37 PM

Hi

The BaseMapper has implementation of applying the same mapping being used to collection of models and entities.
ItemMapper is specific mapping implementation (in this case I use flat/unflat)...

My thinking was based on premise that most of the time you have a view model (e.g. in ASP.NET MVC) which is used in create/edit view, but the collection of that view model is shown in the inder/list view...
So the same mapping needs to be applied to a single view model as well as to a collection of them. By implementing collections mapping in base abstract class allows me to only specify non collective mappings in more specific mapper....

And all methods are virtual so I can override the default behavior as needed also...

For me, this works like a charm so far!