Entity Framework + AutoMapper (Entity для DTO и DTO для объекта)
У меня есть некоторые проблемы с использованием EF с AutoMapper. =/
например:
У меня есть 2 связанных объекта (Клиенты и Заказы)
и они являются классами DTO:
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
Я что-то делаю неправильно?
Спасибо в Advance!
Ответы
Ответ 1
Проблема, с которой я столкнулась, связана с обновлениями ссылок EntityCollection. AutoMapper создает новый экземпляр отношения при сопоставлении от DTO к Entity, и это не радует EF.
Что я решил решить, была настройка AutoMapper для использования целевого значения для моих свойств EntityCollection. В вашем случае:
Mapper.CreateMap< CustomerDTO , Customers >().ForMember(c => c.Orders, o => o.UseDestinationValue());
Таким образом, AM не будет создавать новый экземпляр EntityCollection и будет использовать тот, который пришел с исходным объектом Customer.
Я все еще работаю над тем, чтобы автоматизировать это, но на данный момент он решает мою проблему.
Ответ 2
Ваша проблема в том, что Automapper теряет EntityKey, связанный с записью. Поскольку EntityFramework по умолчанию не обрабатывает POCO (обычный объект CLR)
Джей Циммерман имеет хороший пример здесь, как справиться с этим. gd/4NIcj
Также из Jaroslaw Kowalski (часть команды EF, я считаю) есть этот пример использования POCO в EF, который может хорошо перевести на использование Automapper (у меня еще не было возможности попробовать): http://blogs.msdn.com/jkowalski/archive/2008/09/09/persistence-ignorance-poco-adapter-for-entity-framework-v1.aspx
Ответ 3
Я не уверен, в чем проблема, но - когда я хотел использовать LINQToEntities (перешел на NHibernate),
Мне удалось успешно использовать automapper.
Взгляните на код:
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);
}
}
Это довольно загадочно, всегда воссоздает сопоставления, но это сработало. Надеюсь, это поможет.:)
Ответ 4
Попробуйте сопоставить существующий объект:
entity = Mapper.Map<MyDTO, NyEntity>(dto, entity);
И сохраните Ignore() на месте.
http://groups.google.com/group/automapper-users/browse_thread/thread/24a90f22323a27bc?fwc=1&pli=1
Ответ 5
AutoMapper очень выразителен, когда дело доходит до ошибки отображения. внимательно прочитайте сообщение об исключении.
Еще одна важная вещь - помнить, что вы вызываете Mapper.AssertConfigurationIsValid(); после создания сопоставлений. он дает ошибку, если отображение неверно, тем самым предотвращая исключение позже в среде выполнения приложения.
Ответ 6
Вы должны игнорировать сопоставление некоторых свойств объекта, например:
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());
Если вы просмотрите сообщение из исключения, созданного Automapper, вы должны увидеть свойства объекта, которые нельзя сопоставить, и игнорировать их, как указано выше.
Ответ 7
Теперь, с новой версией AutoMapper, рекомендуется использовать Queryable-Extensions:
При использовании ORM, например NHibernate или Entity Framework, с Стандартные функции Mapper.Map AutoMapper, вы можете заметить, что ORM запросит все поля всех объектов в графе, когда AutoMapper пытается сопоставить результаты с типом адресата.
Если ваш ORM предоставляет IQueryables, вы можете использовать AutoMapper Вспомогательные методы QueryableExtensions для устранения этой ключевой боли.
.ProjectTo() будет показывать механизм преобразования AutoMapper для выделения предложения select в IQueryable, который будет информировать объект что ему нужно только запросить столбец Name элемента таблицу, так же, как если бы вы вручную спроецировали IQueryable на OrderLineDTO с предложением Select.
Создайте сопоставление:
Mapper.CreateMap<Customer, CustomerDto>();
И запрос проекта к dto:
var customerDto =
session.Query<Customer>().Where(customer => customer.Id == id)
.Project().To<CustomerDto>()
.Single();
Ответ 8
Как вы можете прочитать здесь, вам нужно сделать следующее
Вы можете обновлять объекты с помощью AutoMapper. Вот как: передать и DTO, и объект объекта в метод AutoMapper Map. Что делает этот код:
custExisting = Mapper.Map(Of CustomerDTO, Customer)(custDTO, custExisting)
Также остерегайтесь проблем с отображением, таких как описанный здесь
Эти советы работали для меня.