Как сопоставлять списки с помощью ValueInjector

#asp.net #asp.net-mvc #asp.net-mvc-3 #valueinjecter

#asp.net #asp.net-mvc #asp.net-mvc-3 #valueinjecter

Вопрос:

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

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

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

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

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

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

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

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

Комментарии:

1. вы также можете посмотреть здесь: valueinjecter.codeplex.com / … если вы хотите что-то более похожее на automapper

2. @ChuckNorris: Вы пропустили тег «valueinjector»? 😉

3. да, я дал вам ссылку на страницу valueinjector, там показано, как использовать valueinjector более автоматизированным способом. Umbraco CMS использует этот подход

Ответ №1:

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

 IEnumerable<Category> categoryList = categoryService.GetAll();
IList<CategoryViewModel> viewModelList = categoryList
    .Select(x => new CategoryViewModel().InjectFrom(x)).Cast<CategoryViewModel>()
    .ToList();
 

Комментарии:

1. Вместо этого создайте метод расширения, и вы получите 1 (чтобы избежать повторного кода)

2. Спасибо: я сделал это, но затем я получаю следующее: 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>) для кастинга будет достаточным?

3. @jgauffin: я понятия не имею, как создать расширение 🙂 Тебе хочется это сделать?

4. @BrendanVogt: смотрите мой отдельный ответ.

5. @BrendanVogt Я добавил приведение<T> в ответы Дарина, попробуйте сейчас

Ответ №2:

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

Комментарии:

1. по умолчанию InjectFrom работает только для типов объектов, он ничего не будет делать с int и string

2. @ChuckNorris: Странно. Я тестирую код перед его публикацией. Работал нормально. Должно быть, ошибка в моей копии valueinjecter. В любом случае, тип не важен. Пример отлично работает для коллекций

3. Можно ли расширить этот метод расширения, чтобы мы могли вводить из нескольких источников, где каждый источник представляет собой список? Спасибо.

4. @jgauffin спасибо за методы расширения, облегчающие мою жизнь!

5. Ha! Нашел этот ответ снова почти 3 года спустя. Я забыл, что в прошлом я нашел ваши методы расширения и реализовал их. Совсем недавно я сменил работу и перешел на использование InjectFromList (я слегка переименовал его, чтобы помочь другим членам команды найти его), и его там не было. Это простое решение, которое так легко интегрируется в вашу повседневную работу, что вы забываете о его существовании!

Ответ №3:

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

 public static object InjectCompleteFrom(this object target, object source)
{
    if (target.GetType().IsGenericType amp;amp;
        target.GetType().GetGenericTypeDefinition() != null amp;amp; 
        target.GetType().GetGenericTypeDefinition().GetInterfaces() != null amp;amp;
        target.GetType().GetGenericTypeDefinition().GetInterfaces()
              .Contains(typeof(IEnumerable)) amp;amp; 
        source.GetType().IsGenericType amp;amp;
        source.GetType().GetGenericTypeDefinition() != null amp;amp;
        source.GetType().GetGenericTypeDefinition().GetInterfaces() != null amp;amp;
        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);
    }
}    
 

Комментарии:

1. Работает ли этот ответ над object тем, какой тип коллекции нравится IEnumerable или ICollection ?

Ответ №4:

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

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

Комментарии:

1. как насчет сейчас? @jgauffin

Ответ №5:

Создайте универсальный сопоставитель списков:

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