【问题标题】:How to map lists with ValueInjector如何使用 ValueInjector 映射列表
【发布时间】:2011-10-24 07:21:58
【问题描述】:

我正在使用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);

【问题讨论】:

  • 如果你想要类似 automapper 的东西,你也可以看这里:valueinjecter.codeplex.com/…
  • @ChuckNorris:你错过了标签“valueinjector”吗? ;)
  • 是的,我给了你一个指向 valueinjecter 页面的链接,这里展示了如何以更自动化的方式使用 valueinjecter。 Umbraco CMS 使用这种方法

标签: asp.net asp.net-mvc asp.net-mvc-3 valueinjecter


【解决方案1】:

AFAIK 值注入器不支持像 AutoMapper 这样的自动集合映射,但您可以使用简单的 LINQ 表达式并对每个元素进行操作:

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

【讨论】:

  • 创建一个扩展方法,你会得到+1(避免重复代码)
  • 谢谢:我这样做了,但后来我得到了以下信息:Cannot implicitly convert type 'System.Collections.Generic.List&lt;object&gt;' to 'System.Collections.Generic.IList&lt;MyProject.Web.Common.ViewModels.CategoryViewModel&gt;'. An explicit conversion exists (are you missing a cast?)。将(IList&lt;CategoryViewModel&gt;) 用于铸造就足够了吗?
  • @jgauffin:我不知道如何创建扩展 :) 你想做吗?
  • @BrendanVogt:请参阅我的单独答案。
  • @BrendanVogt 我在 Darin 的答案中添加了 Cast,立即尝试
【解决方案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&lt;T&gt; 是功能最少的接口,但有一个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);

【讨论】:

  • 默认的 InjectFrom 只适用于对象类型,它不会对 int 和 string 做任何事情
  • @ChuckNorris:奇怪。我在发布之前测试运行代码。工作得很好。一定是我的 valueinjecter 副本中的错误。无论如何,类型并不重要。该示例适用于集合
  • 这个扩展方法是否可以扩展,以便我们可以从多个来源注入,每个来源都是一个列表?谢谢。
  • @jgauffin 感谢扩展方法,让我的生活更轻松!
  • 哈!将近 3 年后的今天,再次找到了这个答案。我忘记了我过去找到了您的扩展方法并实现了它们。就在最近我换了工作并开始使用 InjectFromList(我稍微重命名了它以帮助其他团队成员找到它)但它不存在。这是一个流畅的解决方案,与您的日常工作无缝集成,以至于您忘记了它的存在!
【解决方案3】:

使用这个函数定义

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

【讨论】:

  • 此答案是否适用于 object 是任何类型的集合,如 IEnumerableICollection
【解决方案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;
}

【讨论】:

  • 现在怎么样? @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 类,并同时调用 MapMapList

var mydtos = ValueMapper.MapList<MyDto>(dtos);
var mydto = ValueMapper.Map<MyDto>(dto);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 1970-01-01
    相关资源
    最近更新 更多