shkipper
shkipper

Reputation: 1413

Entity Framework + AutoMapper ( Entity to DTO and DTO to Entity )

I've got some problems using EF with AutoMapper. =/

for example :

I've got 2 related entities ( Customers and Orders ) and they're DTO classes :


class CustomerDTO
{
   public string CustomerID {get;set;}
   public string CustomerName {get;set;}
   public IList< OrderDTO > Orders {get;set;}
}

class OrderDTO { public string OrderID {get;set;} public string OrderDetails {get;set;} public CustomerDTO Customers {get;set;} }

//when mapping Entity to DTO the code works Customers cust = getCustomer(id); Mapper.CreateMap< Customers, CustomerDTO >(); Mapper.CreateMap< Orders, OrderDTO >(); CustomerDTO custDTO = Mapper.Map(cust);

//but when i try to map back from DTO to Entity it fails with AutoMapperMappingException. Mapper.Reset(); Mapper.CreateMap< CustomerDTO , Customers >(); Mapper.CreateMap< OrderDTO , Orders >(); Customers customerModel = Mapper.Map< CustomerDTO ,Customers >(custDTO); // exception is thrown here

Am I doing something wrong?

Thanks in Advance !

Upvotes: 23

Views: 61610

Answers (8)

Dvor_nik
Dvor_nik

Reputation: 1161

Now, with new version of AutoMapper, the recommended way is using Queryable-Extensions:

When using an ORM such as NHibernate or Entity Framework with AutoMapper's standard Mapper.Map functions, you may notice that the ORM will query all the fields of all the objects within a graph when AutoMapper is attempting to map the results to a destination type.

If your ORM exposes IQueryables, you can use AutoMapper's QueryableExtensions helper methods to address this key pain.

The .ProjectTo() will tell AutoMapper's mapping engine to emit a select clause to the IQueryable that will inform entity framework that it only needs to query the Name column of the Item table, same as if you manually projected your IQueryable to an OrderLineDTO with a Select clause.

Create a mapping:

Mapper.CreateMap<Customer, CustomerDto>();

And project query to dto:

var customerDto = 
   session.Query<Customer>().Where(customer => customer.Id == id)
   .Project().To<CustomerDto>()
   .Single();

Upvotes: 3

Dmytro I.
Dmytro I.

Reputation: 314

As you can read here you need to do the following

You can update entities with AutoMapper. Here's how: pass both the DTO and the entity object to AutoMapper's Map method. That's what this code does:

custExisting = Mapper.Map(Of CustomerDTO,  Customer)(custDTO, custExisting)

Also beware of mapping issues like the one described here

These tips worked for me.

Upvotes: 0

ishakkulekci
ishakkulekci

Reputation: 871

Try mapping to an existing object:

entity = Mapper.Map<MyDTO, NyEntity>(dto, entity); 

And keep the Ignore()'s in place.

http://groups.google.com/group/automapper-users/browse_thread/thread/24a90f22323a27bc?fwc=1&pli=1

Upvotes: 6

codrin
codrin

Reputation: 21

You should ignore mapping of some entity properties like so:

Mapper.CreateMap<CustomerDto, Customer>()
                .ForMember(dest => dest.EntityKey, opt => opt.Ignore())
                .ForMember(dest => dest.Licenses, opt => opt.Ignore())
                .ForMember(dest => dest.AccessCodes, opt => opt.Ignore());

If you examine the message from the exception thrown by Automapper, you should see the entity properties that cannot be mapped and ignore them as above.

Upvotes: 2

Pablo Montilla
Pablo Montilla

Reputation: 3050

The problem I had was related to updates to EntityCollection references. AutoMapper creates a new instance of the relation when mapping from the DTO to the Entity, and that doesn't please the EF.

What solved my problem was configuring AutoMapper to use the destination value for my EntityCollection properties. In your case:

Mapper.CreateMap< CustomerDTO , Customers >().ForMember(c => c.Orders, o => o.UseDestinationValue());

That way AM will not create a new EntityCollection instance, and will use that wich came with the original Customer entity.

I'm still working for a way to automate this, but for now it solves my problem.

Upvotes: 21

user203510
user203510

Reputation: 51

Your problem is because Automapper loses the EntityKey associated with the record. As the EntityFramework does not by default handle POCO's (Plain Old CLR Object)

Jay Zimmerman has a good example here of how to handle this from is. gd /4NIcj Also from Jaroslaw Kowalski (part of the EF team I believe ) has this example for using POCO's within EF, which may translate well to use with Automapper (I've not yet had a chance to try it) : http://blogs.msdn.com/jkowalski/archive/2008/09/09/persistence-ignorance-poco-adapter-for-entity-framework-v1.aspx

Upvotes: 5

geva30
geva30

Reputation: 345

AutoMapper is very expressive when it comes to mapping error. read the exception message carefully.

another important thing is to remember to call Mapper.AssertConfigurationIsValid(); after creating the mappings. it gives an error if the mapping is wrong, thus preventing an exception later in the application runtime.

Upvotes: 2

Arnis Lapsa
Arnis Lapsa

Reputation: 47567

I'm not sure what your problem is, but - when i wanted to use LINQToEntities (switched to NHibernate),
i managed to use automapper with success.

Take a look at code:

public class SimpleMapper<TFrom, TTo>
{
    public static TTo Map(TFrom fromModel)
    {
        Mapper.CreateMap<TFrom, TTo>();
        return Mapper.Map<TFrom, TTo>(fromModel);
    }

    public static IList<TTo> MapList(IList<TFrom> fromModel)
    {
        Mapper.CreateMap<TFrom, TTo>();
        return Mapper.Map<IList<TFrom>, IList<TTo>>(fromModel);
    }
}

public class RepositoryBase<TModel, TLINQModel>
{
    public IList<TModel> Map<TCustom>(IList<TCustom> model)
    {
        return SimpleMapper<TCustom, TModel>.MapList(model);
    }

    public TModel Map(TLINQModel model)
    {
        return SimpleMapper<TLINQModel, TModel>.Map(model);
    }

    public TLINQModel Map(TModel model)
    {
        return SimpleMapper<TModel, TLINQModel>.Map(model);
    }

    public IList<TModel> Map(IList<TLINQModel> model)
    {
        return SimpleMapper<TLINQModel, TModel>.MapList(model);
    }

    public IList<TLINQModel> Map(IList<TModel> model)
    {
        return SimpleMapper<TModel, TLINQModel>.MapList(model);
    }
}

It's quite cryptic, always recreates mappings, but it worked. I hope it helps somehow. :)

Upvotes: 4

Related Questions