Automapper: problème de mappage avec héritage et classe de base abstraite sur les collections avec Entity Framework 4 Proxy Pocos

J'ai un problème en utilisant AutoMapper (qui est une excellente technologie) pour mapper un objet d'affaires à un DTO où j'ai l'héritage d'une classe de base abstraite dans une collection.

Voici mes objets:

abstract class Payment
class CashPayment : Payment
class CreditCardPayment : Payment

j'ai aussi un objet de facture qui contient une collection de paiements comme so:

    public class Invoice
    {
       ... properties...

       public ICollection<Payment> Payments { get; set; }
    }

j'ai aussi les versions DTO correspondantes de chacun de ces objets.

l'objet DtoInvoice est défini comme:

[DataContract]
public class DtoInvoice
{
   ...properties...

   [DataMember]
   public List<DtoPayment> Payments { get; set; }
}

voici à quoi ressemblent mes définitions de Mapper:

Mapper.CreateMap<Invoice, DtoInvoice>();

Mapper.CreateMap<Payment, DtoPayment>()
  .Include<CashPayment, DtoCashPayment>()
  .Include<CreditCardPayment, DtoCreditCardPayment>();

Mapper.CreateMap<CashPayment, DtoCashPayment>();
Mapper.CreateMap<CreditCardPayment, DtoCreditCardPayment>();

Le code pour effectuer la cartographie ressemble à ceci:

var invoice = repo.GetInvoice(invoiceId);

var dtoInvoice = Mapper.Map<Invoice, DtoInvoice>(invoice);

ainsi par exemple si mon objet de facture contient une collection de paiements spécifiques (par exemple 1 espèces et 1 carte de crédit) quand mapper tente de les mapper, je reçois une erreur que le paiement de classe abstraite ne peut pas être créé. Si je supprime le mot-clé abstrait de l'objet de paiement alors le code fonctionne mais je reçois seulement une collection de Objet de paiement, Je ne reçois pas leurs objets spécifiques (espèces et paiements par carte de crédit).

alors la question Est: Comment puis-je obtenir AutoMapper pour cartographier les types de paiement spécifiques et non la classe de base?


mise à Jour

j'ai creusé un peu plus et je pense que je vois un problème mais je ne suis pas sûr comment je peux résoudre cela avec AutoMapper. Je pense que c'est plus un truc D'EF et pas la faute D'AutoMapper. : -)

Dans mon code j'utilise de l'Entité Cadre 4 pocos par procuration avec chargement paresseux.

donc quand j'essaye de mapper une entity retournée de EF qui est un proxy POCO il obtient ce drôle de type comme:

System.Data.Entity.DynamicProxies.CashPayment_86783D165755C316A2F58A4343EEC4842907C5539AF24F0E64AEF498B15105C2

donc, ma théorie est que lorsque AutoMapper tente de mapper CashPayment vers DtoCashPayment et que le paiement transmis est du type proxy, AutoMapper le voit comme une "non-correspondance" et ensuite mappe le type de paiement Générique. Mais puisque le paiement est une classe abstraite AutoMapper bombes avec un " système.InvalidOperationException: Les Instances de classes abstraites ne peuvent pas être créées." exception.

donc la question Est: y a-t-il un moyen pour moi D'utiliser AutoMapper pour mapper les objets proxy EF POCO vers Dtos.

38
demandé sur Ken Burkhardt 2010-08-09 20:04:07

5 réponses

cette réponse arrive "un peu" en retard car je viens de faire face au même problème avec les mandataires EF4 POCO.

je l'ai résolu en utilisant un convertisseur personnalisé appelle Mapper.DynamicMap<TDestination>(object source) pour invoquer la conversion de type runtime, plutôt que le .Include<TOtherSource, TOtherDestinatio>().

Il fonctionne très bien pour moi.

dans votre cas, vous définiriez le convertisseur suivant:

class PaymentConverter : ITypeConverter<Payment, DtoPayment> {
    public DtoPayment Convert( ResolutionContext context ) {
        return Mapper.DynamicMap<DtoPayment>( context.SourceValue );
    }
}

puis:

Mapper.CreateMap<Payment, DtoPayment>().ConvertUsing<PaymentConverter>();
Mapper.CreateMap<CashPayment, DtoCashPayment>();
Mapper.CreateMap<CreditCardPayment, DtoCreditCardPayment>();
15
répondu oli 2010-12-23 11:26:19

J'ai aussi essayé L'exemple D'Olivier et j'ai eu les mêmes erreurs de StackOverflow. J'ai également essayé la solution de subkamran mais pas de chance car je n'utilise pas une classe de base de la génération de code de modèle d'entité. Automapper explose toujours. Jusqu'à ce que je trouve une meilleure solution, j'ai juste défini le contexte pour ne pas créer de mandataires lorsque je crée un objet contextuel.

model.Configuration.ProxyCreationEnabled = false; 
model.Configuration.LazyLoadingEnabled = true; 

je voudrais aussi voir une réponse au problème peut-être en utilisant quelque chose construire dans Automapper...

mise à jour: le La pré-version D'Automapper corrige ce problème et permet à la mapping de couvrir une DynamicProxy sans configuration supplémentaire.

la version dans laquelle cela fonctionne est 2.2.1

15
répondu chrislhardin 2013-02-13 00:10:05

en me basant sur la réponse D'Olivier, je n'ai pas pu faire en sorte que la sienne fonctionne dans mon contexte... il a continué dans une boucle infinie et a lancé un StackOverflowException.

Dans cet exemple, AbstractClass est ma classe de base et AbstractViewModel est mon modèle de vue de base (non marqué comme abstract vous l'esprit).

Cependant, j'ai fait le faire fonctionner à l'aide de cette hackish à la recherche converter:

    public class ProxyConverter<TSource, TDestination> : ITypeConverter<TSource, TDestination>
        where TSource : class
        where TDestination : class
    {
        public TDestination Convert(ResolutionContext context)
        {
            // Get dynamic proxy base type
            var baseType = context.SourceValue.GetType().BaseType;

            // Return regular map if base type == Abstract base type
            if (baseType == typeof(TSource))
                baseType = context.SourceValue.GetType();

            // Look up map for base type
            var destType = (from maps in Mapper.GetAllTypeMaps()
                           where maps.SourceType == baseType
                           select maps).FirstOrDefault().DestinationType;

            return Mapper.DynamicMap(context.SourceValue, baseType, destType) as TDestination;
        }
    }

    // Usage

    Mapper.CreateMap<AbstractClass, AbstractViewModel>()
        .ConvertUsing(new ProxyConverter<AbstractClass, AbstractViewModel>());

Donc,DerivedClassA carte normalement, mais un DynamicProxy_xxx sera également cartographié correctement car ce code inspecte son type de base (DerivedClassA).

S'il vous plaît, s'il vous plaît, s'il vous plaît montrez-moi que je n'ai pas à faire cette foutue recherche. Je ne connais pas assez D'Automappeur pour réparer la réponse D'Olivier correctement.

14
répondu kamranicus 2011-04-21 20:53:42

j'ai rencontré le même problème avec Entity Framework proxies, mais je ne voulais pas passer à une version pré-release D'AutoMapper. J'ai trouvé un travail simple mais légèrement moche pour la version 2.2.0. J'essayais de passer d'un DTO à un objet de remplacement EF existant, et j'ai eu des erreurs à propos de manquer un mapping pour le nom de classe de remplacement moche. Ma solution était d'utiliser une surcharge les types de béton spécifiés que j'avais cartographiés manuellement:

Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));
10
répondu davesw 2012-11-21 22:09:28

je viens de faire face au même problème avec la cartographie dynamique EF mandataires à des modèles de vue dans l'application MVC.

j'ai trouvé une solution facile en utilisant Mappeur.DynamicMap () pour ce problème. Voici mon code:

conversion de la classe Dynamic proxy à la classe ViewModel:

// dynamic proxy instance
WebService webService = _repWebService.GetAll().SingleOrDefault(x => x.Id == id);

//mapping
FirstStepWebServiceModel model = Mapper.DynamicMap<FirstStepWebServiceModel>(webService);

conversion de la classe ViewModel à la classe EF Dynamic Proxy:

[HttpPost]
public ActionResult FirstStep(FirstStepWebServiceModel input)
{
    // getting the dynamic proxy from database
    WebService webService = _repWebService.GetAll().Single(x => x.Id == input.WebServiceId);

    // mapping the input ViewModel class to the Dynamic Proxy entity
    Mapper.DynamicMap(input, webService);
}

espérons que cet exemple vous aidera

6
répondu Ilya Schukin 2013-06-17 10:08:19