Как я могу использовать Automapper для сопоставления объекта с неизвестным типом назначения? - PullRequest
6 голосов
/ 05 октября 2010

Рассмотрим следующий сценарий. У меня есть несколько классов, которые имеют общий базовый класс, и я определил отображение автомпера для каждого производного класса. Примерно так:

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 требуют, чтобы я либо знал тип назначения во время компиляции, либо чтобы объект этого типа был доступен во время выполнения. Это серьезно расстраивает, так как я определил только одну карту для каждого типа источника. AutoMapper должен иметь возможность выводить тип назначения, учитывая только тип источника.

Есть ли хороший способ обойти это? Я хочу избежать создания словаря, сопоставляющего типы источников с типами назначения. Хотя это будет работать, это будет означать, что мне по сути придется определить два сопоставления для каждого типа источника.

Ответы [ 3 ]

11 голосов
/ 07 октября 2010

Вы можете получить доступ к отображениям, хранящимся в 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);
}
3 голосов
/ 05 октября 2010

Вы можете перевернуть его и попросить 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 иерархии.

1 голос
/ 05 октября 2010

Я думаю, Mapper.DynamicMap() и его различные перегрузки - это то, что вы ищете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...