39 votes

Automapper: problème de mappage avec l'héritage et la classe de base abstraite sur les collections avec les pocos de proxy Entity Framework 4

Je rencontre un problème à l'aide de AutoMapper (ce qui est une excellente technologie) à la carte business object pour un DTO où j'ai héritage d'une classe de base abstraite au sein d'une collection.

Voici mes objets:

abstract class Payment
class CashPayment : Payment
class CreditCardPayment : Payment

J'ai aussi une facture de l'objet qui contient une collection de paiements comme suit:

    public class Invoice
    {
       ... properties...

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

J'ai aussi correspondant DTO versions de chacun de ces objets.

Le DtoInvoice objet est défini comme:

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

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

C'est ce que mon Mappeur de définitions de ressembler à:

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

Donc par exemple si ma facture d'objet contient une collection de paiements spécifiques (disons 1 argent et 1 carte de crédit) lorsque mappeur essaie de mapper j'obtiens une erreur que la classe abstraite de Paiement ne peut pas être créé. Si je retire le mot clé abstract de l'objet du Paiement, puis le code fonctionne, mais je ne reçois une collection d'objet du Paiement, je ne reçois pas leurs objets spécifiques (la Trésorerie et les paiements par Carte de Crédit).

La question est donc: Comment puis-je obtenir AutoMapper de la carte des types de paiement spécifiques et non pas la classe de base?


Mise à jour

J'ai fait un peu plus de creuser, et que je vois un problème, mais je suis pas sûr de savoir comment je peux résoudre ce avec AutoMapper. Je pense que c'est plus un EF chose et de ne pas AutoMapper la faute. :-)

Dans mon code j'utilise Entity Framework 4 Proxy POCOs avec un chargement différé.

Donc, lorsque j'essaie de mapper une entité retourné à partir de EF qui est un proxy POCO qu'il obtient de cette drôle à la recherche de ce type:

System.Data.Entity.DynamicProxies.CashPayment_86783D165755C316A2F58A4343EEC4842907C5539AF24F0E64AEF498B15105C2

Si ma théorie est que, lorsque AutoMapper essaie de carte CashPayment à DtoCashPayment et le paiement transmis est le type de proxy AutoMapper le voit comme un "non match" et puis des cartes génériques type de Paiement. Mais depuis de Paiement est une classe abstraite AutoMapper bombes avec un "Système.InvalidOperationException: les Instances de classes abstraites ne peuvent pas être créés." l'exception".

La question est donc: Est-il un moyen pour moi de l'utiliser AutoMapper à la carte EF POCO objets proxy à l'Otd.

15voto

Olivier Points 186

Cette réponse est un peu tardive, car je viens de faire face au même problème avec les proxys EF4 POCO.

Je l'ai résolu en utilisant un convertisseur personnalisé qui appelle Mapper.DynamicMap<TDestination>(object source) pour appeler la conversion de type à l'exécution, plutôt que les .Include<TOtherSource, TOtherDestinatio>() .

Ça fonctionne 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 );
    }
}
 

Et alors:

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

15voto

chrislhardin Points 731

J'ai aussi essayé Olivier de l'exemple et a obtenu le même StackOverflow erreurs. J'ai aussi essayé subkamran de la solution, mais pas de chance il que je ne suis pas à l'aide d'une classe de base de l'entité de modèle de génération de code. Automapper souffle toujours en place. En attendant que je trouve une meilleure solution, je viens de définir le Contexte pour ne pas créer de Procurations quand je crée un objet de Contexte.

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

Je tiens également à voir une réponse au problème peut-être à l'aide de quelque chose de construire en Automapper...

Mise à JOUR: La Pré-version de Automapper résout ce problème et permet pour la cartographie de la couverture d'un DynamicProxy sans configuration supplémentaire.

La libération de cette œuvres est 2.2.1

13voto

subkamran Points 1555

La construction d'Olivier de réponse, je ne pouvais pas obtenir son pour travailler dans mon contexte... il continué dans une boucle infinie et jeta un StackOverflowException.

Dans cet exemple, AbstractClass est ma classe de base et de l' AbstractViewModel est ma base de modèle de vue (pas 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, un DerivedClassA sera carte normalement, mais un DynamicProxy_xxx sera également mapper correctement ce code inspecte son type de base (DerivedClassA).

S'il vous plaît, s'il vous plaît, veuillez me montrer que je n'ai pas à faire cette folle de recherche de la merde. Je ne connais pas assez AutoMapper pour résoudre Olivier de répondre correctement.

10voto

davesw Points 923

J'ai rencontré le même problème avec les mandataires Entity Framework, mais je ne voulais pas passer à une version préliminaire d'AutoMapper. J'ai trouvé un moyen simple, voire légèrement moche, de contourner la version 2.2.0. J'essayais de passer d'un objet DTO à un objet proxy EF existant et j'obtenais des erreurs concernant le manque de mappage du nom de la classe proxy moche. Ma solution consistait à utiliser une surcharge pour spécifier les types concrets réels que j'avais mappés manuellement:

 Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));
 

6voto

Ilya Schukin Points 63

Je viens de faire face au même problème avec le mappage de proxys EF dynamiques à ViewModels dans une application MVC.

J'ai trouvé une solution facile en utilisant Mapper.DynamicMap () pour résoudre ce problème. Voici mon code:

Conversion de proxy dynamique en 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 en proxy dynamique EF:

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

J'espère que cet exemple vous aidera

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X