Может ли automapper сопоставить внешний ключ с объектом, используя репозиторий?

Сначала я пробую код Entity Framework CTP4. Предположим, у меня есть:

public class  Parent
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Child
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Parent Mother { get; set; }
}

public class TestContext : DbContext
{
    public DbSet<Parent> Parents { get; set; }
    public DbSet<Child> Children { get; set; }
}

public class ChildEdit
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int MotherId { get; set; }
}

Mapper.CreateMap<Child, ChildEdit>();

Сопоставление с моделью редактирования не является проблемой. На моем экране я выбираю мать с помощью некоторого элемента управления (раскрывающийся список, автозаполнение и т. д.), и идентификатор матери публикуется сзади:

[HttpPost]
public ActionResult Edit(ChildEdit posted)
{
    var repo = new TestContext();

    var mapped = Mapper.Map<ChildEdit, Child>(posted);  // <------- ???????
}

Как мне решить последнее сопоставление? Я не хочу помещать Mother_Id в дочерний объект. Пока я использую это решение, но я надеюсь, что его можно решить в Automapper.

        Mapper.CreateMap<ChildEdit, Child>()
            .ForMember(i => i.Mother, opt => opt.Ignore());

        var mapped = Mapper.Map<ChildEdit, Child>(posted);
        mapped.Mother = repo.Parents.Find(posted.MotherId);

EDIT Это работает, но теперь я должен сделать это для каждого внешнего ключа (кстати: контекст будет введен в окончательном решении):

        Mapper.CreateMap<ChildEdit, Child>();
            .ForMember(i => i.Mother,
                       opt => opt.MapFrom(o => 
                              new TestContext().Parents.Find(o.MotherId)
                                         )
                      );

То, что я действительно хотел бы, было бы:

        Mapper.CreateMap<int, Parent>()
            .ForMember(i => i, 
                       opt => opt.MapFrom(o => new TestContext().Parents.Find(o))
                      );

        Mapper.CreateMap<ChildEdit, Child>();

Возможно ли это с Automapper?


person John Landheer    schedule 19.08.2010    source источник


Ответы (3)


Во-первых, я предполагаю, что у вас есть интерфейс репозитория, такой как IRepository<T>

После этого создайте следующий класс:

public class EntityConverter<T> : ITypeConverter<int, T>
{
    private readonly IRepository<T> _repository;
    public EntityConverter(IRepository<T> repository)
    {
        _repository = repository;
    }
    public T Convert(ResolutionContext context)
    {
        return _repository.Find(System.Convert.ToInt32(context.SourceValue));       
    }
}

В основном этот класс будет использоваться для выполнения всех преобразований между целым и доменным объектом. Он использует «Id» объекта для загрузки его из репозитория. IRepository будет инжектирован в конвертер с помощью контейнера IoC, но об этом позже.

Давайте настроим сопоставление AutoMapper, используя:

Mapper.CreateMap<int, Mother>().ConvertUsing<EntityConverter<Mother>>();

Вместо этого я предлагаю создать это «общее» сопоставление, чтобы, если у вас есть другие ссылки на «Мать» в других классах, они сопоставлялись автоматически без дополнительных усилий.

Что касается внедрения зависимостей для IRepository, если вы используете Castle Windsor, конфигурация AutoMapper также должна иметь:

IWindsorContainer container = CreateContainer();
Mapper.Initialize(map => map.ConstructServicesUsing(container.Resolve));

Я использовал этот подход, и он работает довольно хорошо.

person psousa    schedule 28.08.2010
comment
Не могли бы вы показать, как использовать ваше решение, когда я хочу сопоставить идентификатор с сущностью? Я имею в виду, как сопоставить, когда я все реализовал? - person user2412672; 23.11.2014

Также можно определить внешний ключ в EF следующим образом:

[ForeignKey("MotherId")]
public virtual Parent Mother { get; set; }
public int MotherId { get; set; }

В этом случае нет необходимости делать дополнительный запрос, чтобы найти Мать. Просто назначьте MotherId ViewModel для MotherId модели.

person VahidN    schedule 12.05.2015

person    schedule
comment
Спасибо. Я хотел избежать необходимости создавать репо в логике сопоставления, потому что это дорого, но из вашего решения я понял, что это неправда. Верно?. Я попытаюсь получить что-то подобное в Automapper и опубликую свое решение. - person John Landheer; 20.08.2010
comment
вам нужно откуда-то взять данные :), обычно я делаю IoC.Resolve(type), и это будет не дорого, потому что контейнер IoC даст уже созданный экземпляр - person Omu; 20.08.2010
comment
Здесь я сделал это универсально, потому что хотел показать, как вы обрабатываете несколько свойств разных типов всего за одну инъекцию, но, конечно, это можно сделать без MakeGenericType, method.invoke и всего такого, но, скорее всего, вы бы для создания нескольких инъекций вы можете посмотреть начало работы с ValueInjecter - person Omu; 20.08.2010
comment
Очень хорошо. Я, вероятно, буду использовать эту технику в ближайшее время. - person John Farrell; 20.08.2010
comment
эта техника на самом деле чертовски крута :), с ней вы скоро забудете о своих сопоставлениях :), вы также можете создать класс сопоставления с методами buildEntity и buildInput, чтобы связать туда все ваши injectFrom‹›, и все, я использую технику показано в классе TinyController из примера asp.net-mvc ValueInjecter, и с использованием IoC на самом деле существует одна реализация IBuilder‹TEntity, TInput›, которая используется в большинстве случаев - person Omu; 20.08.2010