C # Объект Generics, основанный на основе объекта Mapper Вопрос

StackOverflow https://stackoverflow.com/questions/2829624

  •  26-09-2019
  •  | 
  •  

Вопрос

У меня есть необходимость объекта для объекта Mapper в моем приложении. Я попробовал несколько, но не смог найти все, что соответствует моим потребностям, поэтому я пишу свой собственный. В настоящее время у меня есть интерфейс, как приведен ниже:

public interface IMapper<T, R> {
    T Map(R obj);
}

Затем я реализую Accountmapper, который отображает клиента к учетной записи как:

public class AccountMapper : IMapper<Account, Customer> {
    Account Map(Customer obj) {
        // mapping code
    }
}

До сих пор это работает нормально, однако у меня есть несколько исходных объектов, которые отображаются в той же организации назначения. Например, у меня есть платеж и счет, который оба карта к биллистории. Для приведенного выше, чтобы поддержать это, мне нужно сделать два отдельных материала (т. Е. BillHistoryPaymentMapper и BillhistoryinVoicemapper), который в порядке. Тем не менее, я хотел бы иметь возможность реализовать его немного по-разному, как ниже. Только проблема в том, что я не знаю, возможно ли это возможно и если да, я не знаю правильный синтаксис.

public interface IMapper<T> {
    T Map<R>(R obj);
}

public class BillHistoryMapper : IMapper<Account> {
    public BillHistory Map<Invoice>(Invoice obj) {
        // mapping code
    }
    public BillHistory Map<Payment>(Payment obj) {
        // mapping code
    }
}

Хотя первая реализация работает нормально, вторая была бы немного более элегантной. Это возможно, и если так выглядит правильный синтаксис?

редактировать-------

Я ненавижу, когда люди делают это, но, конечно, я забыл упомянуть одну маленькую деталь. У нас есть абстрактный класс между Mapper и интерфейсом для реализации некоторой общей логики по всем монтажам. Итак, моя подпись Mapper на самом деле:

public class BillHistoryMapper : Mapper<BillHistory, Invoice> {
}

где Mapper содержит:

public abstract class Mapper<T, R> : IMapper<T, R> {
    public IList<T> Map(IList<R> objList) {
        return objList.ToList<R>().ConvertAll<T>(new Converter<T, R>(Map));
    }
}
Это было полезно?

Решение

Вам придется использовать свой первый интерфейс и реализовать интерфейс несколько раз на объекте:

public class BillHistoryMapper : IMapper<Account, Invoice>, 
                                 IMapper<Account, Payment> {     
   ...
}

Я бы серьезно подумал о взгляде на Автомат вместо того, чтобы писать свои собственные. В сопоставлении есть много нюансов, что он уже решил, не говоря уже о том, что он прошел через множество производительности, исправлений ошибок и т. Д.

Другие советы

Что касается вашего абстрактного класса, подумайте о том, чтобы избавиться от него и замена его методом расширения. Это позволит вам использовать MapAll Функция независимо от того, реализуете ли вы интерфейс или используете какую-то цепочку наследования.

public static class MapperExtensions
{
    public static IEnumerable<TOutput> MapAll<TInput, TOutput>
        (this IMapper<TInput, TOutput> mapper, IEnumerable<TInput> input)
    {
        return input.Select(x => mapper.Map(x));
    }
}

Теперь это облегчает попытку решить вашу проблему выше, потому что вам больше не нужно наследовать из базового класса, вы теперь можете реализовать интерфейс сопоставления для типов, которые вы хотите отобразить.

public class BillHistoryMapper :
    IMapper<Invoice, BillHistory>, IMapper<Payment, BillHistory>
{
    public BillHistory Map<Invoice>(Invoice obj) {}
    public BillHistory Map<Payment>(Payment obj) {}
}

Также подумайте об изменении вашего IMapper Общие параметры, чтобы быть другим способом (я взял свободу в предыдущие примеры):

public interface IMapper<in TInput, out TOutput>
{
    TOutput Map(TInput input);
}

Причина этого состоит в том, что он напрямую отображает System.Converter<T> Делегат, и вы можете сделать что-то вроде:

IMapper<ObjectA, ObjectB> myAToBMapper = new MyAToBMapper();

ObjectA[] aArray = { new ObjectA(), new ObjectA() };
ObjectB[] bArray = Array.ConvertAll<ObjectA, ObjectB>(aArray, myAToBMapper.Map);

List<ObjectA> aList = new List<ObjectA> { new ObjectA(), new ObjectA() };
List<ObjectB> bList = aList.ConvertAll<ObjectB>(myAToBMapper.Map);

// Or

var aToBConverter = new Converter<ObjectA, ObjectB>(myAToBMapper.Map);
bArray = Array.ConvertAll(aArray, aToBConverter);
bList = aList.ConvertAll(aToBConverter);

Автомат Также было предложено, что сделает вашу жизнь проще. Однако, если вы хотите сохранить свою сопоставление абстракции и иметь свой код Agnostic к вашей стратегии отображения, то очень легко использовать вышеуказанный интерфейс для введения обертки вокруг Automapper. Это также будет означать, что вы можете продолжать использовать MapAll Расширение метода объяснено выше.

public class AutoMapperWrapper<in TInput, out TOutput> : IMapper<TInput, TOutput>
{
    public TOutput Map(TInput input)
    {
        return Mapper.Map<TOutput>(input);
    }
}

Окончательное слово

Также имейте в виду, что вы не всегда собираетесь обнаружить, что ваша стратегия сопоставления будет работать через доску, поэтому не пытайся бороться с вашим доменом и заставить его соответствовать вашей стратегии сопоставления. Одним из конкретных примеров вам, возможно, придется сопоставить из двух входных элементов в одну. Вы, очевидно, можете сделать эту стратегию, но вы можете найти, это становится грязным. В этом конкретном примере считают это слияние.

Ваш второй пример будет работать только с несколькими изменениями:

// you have to include the R type in the declaration of the Mapper interface
public interface IMapper<T, R> {
    T Map<R>(R obj);
}

// You have to specify both IMapper implementations in the declaration
public class BillHistoryMapper : IMapper<Account, Invoice>, IMapper<Account, Payment> {
    public BillHistory Map<Invoice>(Invoice obj) {
        // mapping code
    }
    public BillHistory Map<Payment>(Payment obj) {
        // mapping code
    }
}

Я не уверен, что на самом деле это приготовт вас по поводу существующего шаблона.

Если есть ограниченное количество типов, из которых вы хотите сопоставить, то я бы использовал первый метод объявления входных и выводов в определении интерфейса. Затем Mapper может реализовывать интерфейсы для каждого поддерживающего типа ввода, поэтому ваш BillHistoryMapper будет объявлен как:

public class BillHistoryMapper : IMapper<BillHistory, Invoice>, IMapper<BillHistory, Payment>
{
   ...
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top