Question

J'ai la nécessité d'un objet à mappeur objet dans ma demande. J'ai essayé un peu, mais n'ont pas été en mesure de trouver quelque chose qui correspond à mes besoins, je vous écris moi-même. Actuellement, j'ai une interface comme ci-dessous:

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

Je mets en œuvre alors une AccountMapper qui associe un client à un compte comme:

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

Cela fonctionne bien jusqu'à présent, mais j'ai plusieurs entités source que la carte à la même entité de destination. Par exemple, j'ai un paiement et une facture que les deux carte à BillHistory. Pour ce qui précède pour soutenir cela, je dois faire deux cartographes séparés (ie. BillHistoryPaymentMapper et BillHistoryInvoiceMapper), ce qui est bien. Cependant, j'aimerais être en mesure de mettre en œuvre un peu différemment comme ci-dessous. Le seul problème est que je ne sais pas s'il est possible et si oui, je ne connais pas la syntaxe correcte.

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

Alors que la première mise en œuvre fonctionne bien, la seconde serait un peu plus élégant. Est-ce possible et si oui, quelle serait l'apparence de syntaxe correcte comme?

modifier -------

Je déteste quand les gens font cela, mais bien sûr j'oublié de mentionner un petit détail. Nous avons une classe abstraite entre le cartographe et l'interface pour mettre en œuvre une logique commune à tous les cartographes. Donc, ma signature mappeur est en fait:

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

où Mapper contient:

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));
    }
}
Était-ce utile?

La solution

Vous devrez utiliser votre première interface et implémenter l'interface plusieurs fois sur votre objet:

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

Je sérieusement envisager de jeter un oeil à AutoMapper au lieu d'écrire votre propre. Il y a beaucoup de nuances dans la cartographie qu'il a déjà résolu, sans parler, il a été par de nombreux tests de performance, des corrections de bugs, etc.

Autres conseils

En ce qui concerne votre classe abstraite se débarrasser de envisager et de le remplacer par une méthode d'extension. Cela vous permettra d'utiliser la fonction MapAll indépendamment du fait que vous implémentez l'interface ou d'utiliser une sorte de chaîne d'héritage.

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

Ce sera maintenant plus facile lorsque vous essayez de résoudre votre problème ci-dessus parce que vous n'avez plus à hériter d'une classe de base, vous pouvez maintenant implémenter l'interface de cartographie pour les types que vous souhaitez faire.

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

Voir également changer les paramètres génériques de IMapper être l'autre sens (je pris la liberté dans les exemples précédents):

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

La raison est que les cartes directement, System.Converter<T> délégué et vous pouvez faire quelque chose comme:

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

a également été suggéré AutoMapper qui vous rendra la vie plus facile. Toutefois, si vous vouliez garder votre abstraction de cartographie et d'avoir votre code agnostique à votre stratégie de cartographie alors il est très facile à utiliser l'interface ci-dessus pour injecter une enveloppe autour AutoMapper. Il signifie également que vous pouvez continuer à utiliser la méthode d'extension de MapAll expliqué ci-dessus.

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

Le dernier mot

Gardez également à l'esprit que vous allez pas toujours trouver que votre stratégie de cartographie fonctionnera dans tous les domaines afin de ne pas essayer de combattre votre domaine et le forcer à adapter votre stratégie de cartographie. Un exemple particulier est que vous pourriez avoir à la carte à partir de deux éléments d'entrée en un seul. Vous pouvez évidemment faire tenir sur votre stratégie, mais vous pouvez le trouver devient désordre. Dans cet exemple particulier considérer une fusion.

Votre deuxième exemple fonctionnera avec seulement quelques changements:

// 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
    }
}

Je ne sais pas si cela vous gagne quoi que ce soit sur le modèle existant, cependant.

S'il y a un nombre limité de types que vous souhaitez faire de, alors j'utiliser la première méthode de déclarer les types d'entrée et de sortie dans la définition de l'interface. Ensuite, un mappeur peut implémenter des interfaces pour chaque type d'entrée, il prend en charge, de sorte que votre BillHistoryMapper serait déclaré:

public class BillHistoryMapper : IMapper<BillHistory, Invoice>, IMapper<BillHistory, Payment>
{
   ...
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top