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);
Brendan Vogt
  • 25,678
  • 37
  • 146
  • 234
  • you can also look here: http://valueinjecter.codeplex.com/wikipage?title=Automapper%20Simulation&referringTitle=Home if you want something more like automapper – Omu Oct 24 '11 at 10:50
  • @ChuckNorris: Did you miss the tag "valueinjector"? ;) – jgauffin Oct 24 '11 at 11:12
  • 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 approach – Omu Oct 24 '11 at 11:59

5 Answers5

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();
Omu
  • 69,856
  • 92
  • 277
  • 407
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
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);
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • the default InjectFrom works only for object types, it's not going to do anything with int and string – Omu Oct 24 '11 at 10:54
  • @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 collections – jgauffin Oct 24 '11 at 11:08
  • Can this extension method be expanded so that we can inject from multiple sources where each source is a List? Thanks. – Saxman Jun 26 '12 at 16:32
  • @jgauffin thanks for the extension methods, making my life easier! – Mike Devenney Dec 12 '13 at 03:05
  • BRILLIANT! Worked for me – DJ Burb Mar 19 '14 at 20:05
  • is there a way to enhance the answer with `yield`? – Korayem Apr 24 '16 at 06:38
  • 1
    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! – Mike Devenney Dec 08 '16 at 17:51
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);
    }
}    
M.Babcock
  • 18,753
  • 6
  • 54
  • 84
Suresh Balla
  • 189
  • 2
  • 16
  • Does this answer work on `object` that are any type of collection like `IEnumerable` or `ICollection`? – Korayem Apr 24 '16 at 06:40
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;
}
Korayem
  • 12,108
  • 5
  • 69
  • 56
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);
D.Kempkes
  • 345
  • 3
  • 5