AutoMapper与ValueInjecter的比较

208
每当我在StackOverflow上寻找AutoMapper相关的东西时,我总是会读到一些关于ValueInjecter的内容。
有人能告诉我它们之间的优缺点吗(性能、功能、API使用、可扩展性、测试)?

2
我看到经常提到的另一个是 EmitMapper - adrianbanks
1
关于 Glue 呢?http://glue.codeplex.com/ 看起来也是一个很棒的项目,但我还没有尝试过。不过下个月我会试一下。我也看到了一个叫 EmitMapper 的项目 http://emitmapper.codeplex.com/ - Trygve
4个回答

169

作为ValueInjecter的创造者,我可以告诉你我的初衷是想要一个简单且非常灵活的东西。

我真的不喜欢写很多代码或写出很多猴子代码,比如:

Prop1.Ignore, Prop2.Ignore etc.
CreateMap<Foo,Bar>(); CreateMap<Tomato, Potato>(); etc.

ValueInjecter类似于拥有插件的Mozilla,您可以创建ValueInjections并使用它们。

内置注入用于平铺、反平铺,并且有些注入是用于继承的。

它的工作方式更像是一种方面,您不必指定所有属性1对1,而是执行以下操作:

从源中获取名称以“Id”结尾的所有int属性,转换值并将每个值设置为源对象中具有相同名称但没有Id后缀并且其类型继承自Entity的属性,例如这样的内容。

因此,一个明显的区别是,ValueInjecter甚至在窗体中进行平铺和反平铺操作,这就是它的灵活性所在。

(从对象映射到窗体控件并返回)

Automapper不能在Windows Forms中使用,无法反平铺,但它具有良好的集合映射等功能,因此如果您需要与ValueInjecter一起使用,则只需执行以下操作:

foos.Select(o => new Bar().InjectFrom(o));

您还可以使用ValueInjecter将匿名和动态对象映射到对象。

区别:

  • automapper为每个映射可能性创建配置CreateMap()
  • valueinjector从任何对象注入到任何对象(还有从对象到值类型的情况)
  • automapper内置了平铺,仅适用于简单类型或相同类型,并且它没有反平铺
  • 只有在需要时,您才能使用valueinjecter执行target.InjectFrom<FlatLoopValueInjection>(source); also <UnflatLoopValueInjection>。如果您想要从Foo.Bar.Name的String类型映射到FooBarName的Class1类型,则可以继承FlatLoopValueInjection并指定此内容。
  • automapper默认映射具有相同名称的属性,对于其余属性,您必须一一指定,并执行类似于Prop1.Ignore()、Prop2.Ignore()等的操作。
  • valueinjecter具有默认注入.InjectFrom(),它执行具有相同名称和类型的属性;对于其他所有内容,您都可以使用带有个别映射逻辑/规则的自定义valueinjections创建,这更像是方面,例如从Foo类型的所有属性映射到Bar类型的所有属性。

5
求求你了,告诉我ValueInjector能否将一个深度图形的ViewModel映射到一个深度图形的Business Entity,并且可以自动映射所有完全相同的内容,我只需要指定如何处理不同的部分。我一直希望AutoMapper能够添加这个功能,但它从未实现过,我也没有时间编写自己的自动映射程序。 - Chris Marisic
3
@Chris Marisic,如果您是指深度克隆,您可以使用这个库:http://valueinjecter.codeplex.com/Thread/View.aspx?ThreadId=236126,它可以进行递归克隆,但不支持集合属性。或者您可以使用扁平化视图模型,在扁平化和取消扁平化时使用,这将更加容易。 - Omu
27
“<pedant>” 看起来不错,但也许应该是“ValueInjectOr”呢? “</ pedant>” - Craig Stuntz
1
但出于某种原因,它是 er :) - Omu
我目前正在考虑使用哪一个,其中一个我可以立即看到的相关因素是AutoMapper似乎在定期更新,而ValueInjecter在NuGet上已经近3年没有更新了。 - JsonStatham
显示剩余14条评论

59

由于我从未使用过其他工具,所以我只能谈谈AutoMapper。我在构建AutoMapper时有几个目标:

  • 支持将数据展开(flattening)到简单的DTO对象
  • 支持开箱即用的明显场景(集合,枚举等)
  • 能够在测试中轻松验证映射
  • 允许解决其他地方的值的边缘情况(自定义类型->类型映射,单个成员映射和一些非常疯狂的边缘情况)。

如果你想做这些事情,AutoMapper非常适合你。AutoMapper做不好的事情是:

  • 填充现有对象
  • 展开(unflattening)

原因是我从来没有需要做这些事情。在很大程度上,我们的实体没有设置器,也没有暴露集合等等,这就是为什么它不在那里的原因。我们使用AutoMapper将数据展开到DTO并从UI模型映射到命令消息等等。在这方面,它对我们非常非常好。


1
@Jimmy Bogard,您认为自动映射器的功能列表中是否会包括填充现有对象? - Roman
我还没有尝试过ValueInjecter,但根据我们的需求来看,automapper非常强大。 - richb01
我认为这里最重要的是可验证性。在重命名和重构事物时,这是一个巨大的帮助。 - Kugel

54

我尝试了两者并更喜欢ValueInjecter,因为它非常简单:

myObject.InjectFrom(otherObject);

对于我大部分注入需求而言,这就是需要知道的全部。没有比这更简单和优雅的了。


1
这个对象有扩展方法吗? - Chris Marisic
2
我该如何将我的代码与ValueInjecter解耦?对我来说,似乎总是需要依赖ValueInjecter,例如在我的Web项目中,因为我直接在给定对象上使用ValueInjecter(扩展方法)。 - Rookian
1
@Rookian 说实话,这不是你应该过多考虑的问题。你可以像@Omu提到的那样依赖于接口,这样如果你改变映射器,你可能可以节省一些工作量(也许不太多)。这种依赖关系很难抽象化,除非你想进行全面的AOP,但不幸的是,由于.NET没有正确提供AOP支持,这在许多情况下都是不可行的。现在,你可以使用MVC并编写Action Filters来处理ViewModel/DomainModel映射,从而消除一些映射。 - Chris Marisic
13
为什么包装器是最佳解决方案?如果你想切换 mapper,唯一需要做的就是自己实现 InjectFrom() 扩展方法。 - jgauffin
1
我已经尝试过两种方式,我更喜欢AutoMapper。我在系统的一小部分中使用它来映射实体和Linq2Sql生成的类。像StockTotalQuantity -> stock_size_quantity或UserId -> user_id这样简单的映射在AutoMapper中默认就可以工作。即使添加了转换规则,ValeInjecter也无法正常工作。现在坚持使用AutoMapper。 - Artur Kedzior
显示剩余4条评论

27
这是我一直在研究的问题,对于我的用例来说,valueinjecter似乎是最好的选择。它不需要任何预先设置即可使用(可能会影响性能,但如果实现得当,它可以为将来的调用缓存映射,而不是每���都进行反射),因此在使用之前不需要预定义任何映射。

最重要的是,它允许反向映射。然而,我可能遗漏了什么,因为Jimmy提到他看不到必要使用它的用例,所以也许我有错的模式,但我的用例是我从我的ORM创建ViewModel对象。我然后在网页上显示这个对象。一旦用户完成,我会将ViewModel作为httppost返回,那么这如何转换回原始的ORM类呢?我很想知道使用automapper的模式。对于ValueInjector来说,这很简单,它甚至可以解压。例如创建一个新实体。

由entityframework(模型优先)创建的模型:

public partial class Family
{ 
    public int Id { get; set; }
    public string FamilyName { get; set; }

    public virtual Address Address { get; set; }
}

public partial class Address
{
    public int Id { get; set; }
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string TownCity { get; set; }
    public string County { get; set; }
    public string Postcode { get; set; }

    public virtual Family Family { get; set; }
}

ViewModel(我可以用验证器来修饰):

public class FamilyViewModel
{
    public int Id { get; set; }
    public string FamilyName { get; set; }

    public int AddressId { get; set; }
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string AddressTownCity { get; set; }
    public string AddressCounty { get; set; }
    public string AddressPostcode { get; set; }
}

视图控制器:

    //
    // GET: /Family/Create

    public ActionResult Create()
    {
        return View();
    } 

    //
    // POST: /Family/Create

    [HttpPost]
    public ActionResult Create(FamilyViewModel familyViewModel)
    {
        try
        {
            Family family = new Family();
            family.InjectFrom<UnflatLoopValueInjection>(familyViewModel);
            db.Families.Add(family);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }
在我看来,这再简单不过了?
(那么问题来了,为什么我遇到这个模式时(似乎很多人都是如此),它没有被视为AutoMapper的价值所在?)
然而,如果您想使用这种描述的模式,那么我强烈推荐使用valueinjecter。

1
很可能你也应该在另一个标记为asp.net-mvc和最佳实践的问题中提出这个问题,ViewModel... 目前我没有看到任何问题,只要它对你有效,但我相信有人可能持有不同的意见。 - Omu
学习了更多的MVC之后,我现在可以回答我的问题了。当你得到一个填充好的视图模型时,更新原始模型的方法是使用MVC提供的UpdateModel()函数。 - DanH
1
UpdateModel() 用于填充表示视图的 Model,并且与执行 Action(MyModelClasss model) 相同。 - Omu
我认为我即将提出的解决方案可能不属于这个线程。但是这是我会做的。我会将这项工作委托给业务服务。因此,MVC对数据模型一无所知,它只知道域模型。业务服务与数据模型和域模型进行通信,并使用Automapper、ValueInjector或您自己的自定义映射器进行单向转换。 - daehaai
1
我认为有这样一个论点,即你不应该仅仅在你的领域模型上设置属性 - 你应该使用为其增加意义的方法。 - Mike Cole
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接