How can I use Automapper to map an object to an unknown destination type?

五迷三道 提交于 2021-01-27 04:54:09

问题


Consider the following scenario. I have a number of classes that share a common base class and I have defined an automapper mapping for each derived class. Something like this:

class A : Base {}

class B : Base {}

class ContractA : ContractBase {}

class ContractB : ContractBase {}

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

So far so good. But now I want to create a method like this:

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

    return Mapper.???
}

The problem is that all of AutoMapper's Map variants require that I either know the destination type at compile time or have an object of that type available at runtime. This is seriously frustrating since I have defined only one map for each source type. AutoMapper should be able to infer the destination type given only the source type.

Is there any good way around this? I want to avoid creating a dictionary mapping source types to destination types. While this would work, it would mean that I'd essentially have to define two mappings for every source type.


回答1:


You could access the mappings stored in 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);
}



回答2:


You can turn it around and ask Base to give you a mapped contract:

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

With this code:

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

UPDATE: if you must separate the logic from the classes, you could use a visitor:

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

This is what it looks like:

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

Now, all the mapper logic is in the MapToContractVisitor class, not in the Base hierarchy classes.




回答3:


I think Mapper.DynamicMap() and its various overloads are what you're looking for.



来源:https://stackoverflow.com/questions/3866425/how-can-i-use-automapper-to-map-an-object-to-an-unknown-destination-type

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!