How to map lists with ValueInjector
Asked Answered
H

5

25

I am using ASP.NET MVC 3.

Can someone please help me clarify what's happening here:

var person = new PersonRepository().Get();

var personViewModel = new PersonViewModel();
personViewModel.InjectFrom<LoopValueInjection>(person)
     .InjectFrom<CountryToLookup>(person);

I have a grid on my Index view. Each row is an instance of a CategoryViewModel. So what I do is to get a list of all the categories and then map each Category to a CategoryViewModel, and then pass this list of CategoryViewModels to the view. Hou would I do a mapping like that?

IEnumerable<Category> categoryList = categoryService.GetAll();

I thought the following would work but it doesn't:

// Mapping
IList<CategoryViewModel> viewModelList = new List<CategoryViewModel>();
viewModelList.InjectFrom(categoryList);
Hebrews answered 24/10, 2011 at 7:21 Comment(3)
you can also look here: valueinjecter.codeplex.com/… if you want something more like automapperAlternate
@ChuckNorris: Did you miss the tag "valueinjector"? ;)Bananas
yes, I gave you a link to an valueinjecter page, there is shown how to use the valueinjecter in a more automated way. Umbraco CMS uses this approachAlternate
D
47

AFAIK value injecter doesn't support automatic collection mapping like AutoMapper but you could use a simple LINQ expression and operate on each element:

IEnumerable<Category> categoryList = categoryService.GetAll();
IList<CategoryViewModel> viewModelList = categoryList
    .Select(x => new CategoryViewModel().InjectFrom(x)).Cast<CategoryViewModel>()
    .ToList();
Dicarlo answered 24/10, 2011 at 7:29 Comment(6)
Create an extension method instead and you'll get +1 (to avoid repeated code)Bananas
Thanks: I did that but then I get the following: Cannot implicitly convert type 'System.Collections.Generic.List<object>' to 'System.Collections.Generic.IList<MyProject.Web.Common.ViewModels.CategoryViewModel>'. An explicit conversion exists (are you missing a cast?). Puting (IList<CategoryViewModel>) for the casting be sufficient?Hebrews
@jgauffin: I have no idea how to create an extension :) You feel like doing it?Hebrews
@BrendanVogt: see my separate answer.Bananas
@BrendanVogt I've added a Cast<T> in Darin's answers, try nowAlternate
@Chuck Norris: Thanks. I had initially put in the Cast, but forgot to put in ToList().Hebrews
B
25
//source list
IEnumerable<string> items = new string[] { "1", "2" };

// target list
List<int> converted = new List<int>();

// inject all
converted.InjectFrom(items);

And the extension method:

public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, IEnumerable<TFrom> from) where TTo : new()
{
    foreach (var source in from)
    {
        var target = new TTo();
        target.InjectFrom(source);
        to.Add(target);
    }
    return to;
}

ICollection<T> is the interface that got least features but a Add method.

Update

An example using more proper models:

var persons = new PersonRepository().GetAll();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(persons);

Update - Inject from different sources

public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, params IEnumerable<TFrom>[] sources) where TTo : new()
{
    foreach (var from in sources)
    {
        foreach (var source in from)
        {
            var target = new TTo();
            target.InjectFrom(source);
            to.Add(target);
        }
    }
    return to;
}

Usage:

var activeUsers = new PersonRepository().GetActive();
var lockedUsers = new PersonRepository().GetLocked();
var personViewModels = new List<PersonViewModel>();

personViewModels.InjectFrom(activeUsers, lockedUsers);
Bananas answered 24/10, 2011 at 8:7 Comment(7)
the default InjectFrom works only for object types, it's not going to do anything with int and stringAlternate
@ChuckNorris: Strange. I testrun the code before posting it. Worked fine. Must be a bug in my copy of valueinjecter. Anyhow, the type is not important. The example works fine for collectionsBananas
Can this extension method be expanded so that we can inject from multiple sources where each source is a List? Thanks.Gallous
@Bananas thanks for the extension methods, making my life easier!Grati
BRILLIANT! Worked for meMarcum
is there a way to enhance the answer with yield?Radarscope
Ha! Found this answer again almost 3 years to the day later. I had forgotten that I found your extension methods in the past and implemented them. Just recently I changed jobs and went to use InjectFromList (I renamed it slightly to help other team members find it) and it wasn't there. It's a smooth solution that so seamlessly integrates with your daily work that you forget it's there!Grati
P
2

Use this function definition

public static object InjectCompleteFrom(this object target, object source)
{
    if (target.GetType().IsGenericType &&
        target.GetType().GetGenericTypeDefinition() != null && 
        target.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
        target.GetType().GetGenericTypeDefinition().GetInterfaces()
              .Contains(typeof(IEnumerable)) && 
        source.GetType().IsGenericType &&
        source.GetType().GetGenericTypeDefinition() != null &&
        source.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
        source.GetType().GetGenericTypeDefinition().GetInterfaces()
              .Contains(typeof(IEnumerable)))
    {
        var t = target.GetType().GetGenericArguments()[0];
        var tlist = typeof(List<>).MakeGenericType(t);
        var addMethod = tlist.GetMethod("Add");

        foreach (var sourceItem in source as IEnumerable)
        {
            var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(sourceItem);
            addMethod.Invoke(target, new[] { e });
        }

        return target;
    }
    else
    {
        return target.InjectFrom(source);
    }
}    
Phosphatize answered 16/3, 2012 at 10:29 Comment(1)
Does this answer work on object that are any type of collection like IEnumerable or ICollection?Radarscope
R
1

For those like me who prefer shortest notations possible

public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, ICollection<TOrig> source) where TTarget : new()
{
    source.Select(r => new TTarget().InjectFrom(r))
       .Cast<TTarget>().ToList().ForEach(e => target.Add(e));
    return target;
}
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, params ICollection<TOrig>[] sources) where TTarget : new()
{
    sources.ToList().ForEach(s => s.ToList().Select(r => new TTarget().InjectFrom(r))
       .Cast<TTarget>().ToList().ForEach(e => target.Add(e)));
    return target;
}
Radarscope answered 24/4, 2016 at 7:59 Comment(1)
how about now? @BananasRadarscope
A
0

Create a generic list mapper:

public class ValueMapper
{
     public static TResult Map<TResult>(object item) where TResult : class
    {
        return item == null ? null : Mapper.Map<TResult>(item);
    }

    public static IEnumerable<TResult> MapList<TResult>(IEnumerable<object> items) where TResult : class
    {
        return items?.Select(i => Mapper.Map<TResult>(i));
    }
}

Now you can reference the ValueMapper class wherever you want, and call both Map and MapList

var mydtos = ValueMapper.MapList<MyDto>(dtos);
var mydto = ValueMapper.Map<MyDto>(dto);
Amoretto answered 14/10, 2019 at 9:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.