Как отображать списки с помощью ValueInjector

Я использую ASP.NET MVC 3.

Может кто-нибудь, пожалуйста, помогите мне прояснить, что здесь происходит:

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

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

У меня есть сетка в представлении Index. Каждая строка является экземпляром CategoryViewModel. Итак, что я делаю, так это получаю список всех категорий, затем сопоставляю каждую Category с CategoryViewModel, а затем передаю этот список CategoryViewModels в представление. Как бы я сделал такое отображение?

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

Я думал, что следующее будет работать, но это не так:

// Mapping
IList<CategoryViewModel> viewModelList = new List<CategoryViewModel>();
viewModelList.InjectFrom(categoryList);

person Brendan Vogt    schedule 24.10.2011    source источник
comment
вы также можете посмотреть здесь: valueinjecter.codeplex.com/, если вы хочу что-то больше похожее на automapper   -  person Omu    schedule 24.10.2011
comment
@ChuckNorris: Вы пропустили тег valueinjector? ;)   -  person jgauffin    schedule 24.10.2011
comment
да, я дал вам ссылку на страницу инжектора значений, там показано, как использовать инжектор значений более автоматизированным способом. Umbraco CMS использует этот подход   -  person Omu    schedule 24.10.2011


Ответы (5)


Инжектор значений AFAIK не поддерживает автоматическое сопоставление коллекций, например AutoMapper, но вы можете использовать простое выражение LINQ и работать с каждым элементом:

IEnumerable<Category> categoryList = categoryService.GetAll();
IList<CategoryViewModel> viewModelList = categoryList
    .Select(x => new CategoryViewModel().InjectFrom(x)).Cast<CategoryViewModel>()
    .ToList();
person Darin Dimitrov    schedule 24.10.2011
comment
Вместо этого создайте метод расширения, и вы получите +1 (чтобы избежать повторения кода) - person jgauffin; 24.10.2011
comment
Спасибо: я так и сделал, но потом получаю следующее: 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?). Поставить (IList<CategoryViewModel>) для кастинга будет достаточно? - person Brendan Vogt; 24.10.2011
comment
@jgauffin: я понятия не имею, как создать расширение :) Хочешь это сделать? - person Brendan Vogt; 24.10.2011
comment
@BrendanVogt: см. мой отдельный ответ. - person jgauffin; 24.10.2011
comment
@BrendanVogt Я добавил Cast‹T› в ответы Дарина, попробуйте сейчас - person Omu; 24.10.2011
comment
@Чак Норрис: Спасибо. Сначала я вставил Cast, но забыл добавить ToList(). - person Brendan Vogt; 24.10.2011

//source list
IEnumerable<string> items = new string[] { "1", "2" };

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

// inject all
converted.InjectFrom(items);

И метод расширения:

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> — это интерфейс с наименьшим количеством функций, но с Add методом.

Обновить

Пример использования более подходящих моделей:

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

Обновление – вставка из разных источников

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

Применение:

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

personViewModels.InjectFrom(activeUsers, lockedUsers);
person jgauffin    schedule 24.10.2011
comment
InjectFrom по умолчанию работает только для типов объектов, он ничего не делает с int и string - person Omu; 24.10.2011
comment
@ChuckNorris: Странно. Я тестирую код перед публикацией. Работал нормально. Должно быть ошибка в моей копии valueinjecter. Впрочем, тип не важен. Пример отлично работает для коллекций - person jgauffin; 24.10.2011
comment
Можно ли расширить этот метод расширения, чтобы мы могли вводить из нескольких источников, где каждый источник является списком? Спасибо. - person Saxman; 26.06.2012
comment
@jgauffin спасибо за методы расширения, облегчающие мою жизнь! - person Mike Devenney; 12.12.2013
comment
БЛЕСТЯЩИЙ! Работал на меня - person DJ Burb; 20.03.2014
comment
есть ли способ улучшить ответ с помощью yield? - person Korayem; 24.04.2016
comment
Ха! Нашел этот ответ снова почти через 3 года на следующий день. Я забыл, что нашел ваши методы расширения в прошлом и реализовал их. Совсем недавно я сменил работу и пошел использовать InjectFromList (я немного переименовал его, чтобы помочь другим членам команды найти его), и его там не было. Это гладкое решение, которое настолько органично интегрируется в вашу повседневную работу, что вы забываете о нем! - person Mike Devenney; 08.12.2016

Используйте это определение функции

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);
    }
}    
person Suresh Balla    schedule 16.03.2012
comment
Работает ли этот ответ на object, которые представляют собой коллекции любого типа, такие как IEnumerable или ICollection? - person Korayem; 24.04.2016

Для таких, как я, которые предпочитают максимально короткие обозначения

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;
}
person Korayem    schedule 24.04.2016
comment
Как насчет сейчас? @jgauffin - person Korayem; 27.04.2016

Создайте общий преобразователь списка:

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

Теперь вы можете ссылаться на класс ValueMapper где угодно и вызывать как Map, так и MapList.

var mydtos = ValueMapper.MapList<MyDto>(dtos);
var mydto = ValueMapper.Map<MyDto>(dto);
person D.Kempkes    schedule 14.10.2019