【问题标题】:How can I use Automapper to map an object to an unknown destination type?如何使用 Automapper 将对象映射到未知的目标类型?
【发布时间】:2010-10-05 18:06:56
【问题描述】:

考虑以下场景。我有许多共享一个公共基类的类,并且我为每个派生类定义了一个自动映射器映射。像这样的:

class A : Base {}

class B : Base {}

class ContractA : ContractBase {}

class ContractB : ContractBase {}

void Foo()
{
    Mapper.CreateMap<A, ContractA>();
    Mapper.CreateMap<B, ContractB>();
}

到目前为止一切顺利。但现在我想创建一个这样的方法:

ContractBase Foo()
{
    Base obj = GetObject();

    return Mapper.???
}

问题是 AutoMapper 的所有 Map 变体都要求我要么在编译时知道目标类型,要么在运行时拥有该类型的对象。这非常令人沮丧,因为我只为每种源类型定义了一个映射。 AutoMapper 应该能够仅根据源类型推断目标类型。

有什么好的办法吗?我想避免创建将源类型映射到目标类型的字典。虽然这可行,但这意味着我基本上必须为每种源类型定义两个映射。

【问题讨论】:

  • 使用 valueinjecter.codeplex.com

标签: c# automapper


【解决方案1】:

您可以访问存储在 AutoMapper 中的映射:

ContractBase Foo() {
  Base obj = GetObject();

  var sourceType = obj.GetType();
  var destinationType = Mapper.GetAllTypeMaps().
    Where(map => map.SourceType == sourceType).
    // Note: it's assumed that you only have one mapping for the source type!
    Single(). 
    DestinationType;

  return (ContractBase)Mapper.Map(obj, sourceType, destinationType);
}

【讨论】:

  • 此解决方案在版本 5 中不起作用,因为我们没有 Mapper.GetAllTypeMaps()
  • @MohammadDayyan:感谢您提供的信息。也许this issue 会有所帮助。
【解决方案2】:

你可以转过来让Base给你一个映射合约:

ContractBase Foo() {
  Base obj = GetObject();
  return obj.ToContract();
}

使用此代码:

abstract class Base {
  public abstract ContractBase ToContract();
}
class A : Base {
  public override ContractBase ToContract() {
    return Mapper.Map<A, ContractA>(this);
  }
}
class B : Base {
  public override ContractBase ToContract() {
    return Mapper.Map<B, ContractB>(this);
  }
}

更新:如果您必须将逻辑与类分开,您可以使用访问者:

ContractBase Foo() {
  Base obj = GetObject();
  var visitor = new MapToContractVisitor();
  obj.Accept(visitor);
  return visitor.Contract;
}

这就是它的样子:

abstract class Base {
  public abstract void Accept(IBaseVisitor visitor);
}
class A : Base {
  public override void Accept(IBaseVisitor visitor) {
    visitor.Visit(this);
  }
}
class B : Base {
  public override void Accept(IBaseVisitor visitor) {
    visitor.Visit(this);
  }
}
interface IBaseVisitor {
  void Visit(A a);
  void Visit(B b);
}
class MapToContractVisitor : IBaseVisitor {
  public ContractBase Contract { get; private set; }
  public void Visit(A a) {
    Contract = Mapper.Map<A, ContractA>(a); 
  }
  public void Visit(B b) {
    Contract = Mapper.Map<B, ContractB>(b);
  }
}

现在,所有映射器逻辑都在MapToContractVisitor 类中,而不是在Base 层次结构类中。

【讨论】:

  • 这很聪明,但不幸的是它对我们不起作用。所讨论的类代表不同系统的消息。他们彼此并不了解对方。
  • 这并不能真正解决原来的问题。我的问题不是我没有办法进行映射,而是我想避免重复工作。使用此解决方案,每次添加一对新类时,我必须做两件事。首先,我必须定义自动映射器映射,然后我必须创建一个访问者方法。我希望有一种方法来定义 just 映射而不是其他任何东西。
  • 无论如何你需要做两件事:Mapper.CreateMap 和后来的Mapper.Map。此代码处理Mapper.Map。例如,您还可以将其全部集中在访问者中,并将Mapper.CreateMap 放在其静态构造函数中。然后,只有一个类知道这些映射。
  • 我想我没有很好地解释这一点。我设想的一个非常常见的场景是必须添加新的映射类对。我想构造这样的东西,在这种情况下,程序员必须编写的唯一行是 Mapper.CreateMap().
  • 我明白你的意思了。您应该以某种方式访问​​ AutoMapper 的源类型映射存储库,以查找并映射到目标类型。我不知道该怎么做。如果不可能,您可以控制映射并在 AutoMapper 之上创建一个了解该信息的抽象。
【解决方案3】:

我认为Mapper.DynamicMap() 及其各种重载正是您要寻找的。​​p>

【讨论】:

  • 这些方法似乎只是将一个对象映射到另一个对象(而不是返回一个新对象)。他们仍然需要目的地类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-27
  • 1970-01-01
相关资源
最近更新 更多