AutoMapper ProjectTo ‹› () не находит карту

У меня есть приложение ASP.NET 5 (работающее на 4.6.2, а не на Core). Я хотел использовать метод ProjectTo ‹> () AutoMapper для проецирования результатов из базы данных на мои модели просмотра.

Я пробовал много тестов, но кажется, что карту невозможно найти только при использовании ProjectTo ‹> (). Использование mapper.Map ‹> () в разных местах с одной и той же моделью и моделью просмотра отлично работает.

Я предполагаю, что что-то не так с тем, как AutoMapper работает с моим DI (Autofac), но я не могу понять, что.

Во всяком случае, код:

Startup.Cs

 public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            (...)

            // Autofac DI
            AutofacContainer = AutofacLoader.Configure(services).Build();

            return AutofacContainer.Resolve<IServiceProvider>();
        }

AutofacLoader.cs

public static ContainerBuilder Configure(IServiceCollection services)
        {
            var builder = new ContainerBuilder();

(...)


            // AutoMapper
            builder.RegisterModule<AutoMapperModule>();

            if (services != null)
            { 
                builder.Populate(services);

            }
            return builder;
        }

AutoMapperModule.cs

public class AutoMapperModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        var mapping = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new Core.Mappings.AutoMapperProfileConfiguration());
            cfg.AddProfile(new Dieet.Core.Mappings.AutoMapperProfileConfiguration());
        });
        builder.RegisterInstance(mapping.CreateMapper()).As<IMapper>().AutoActivate();
    }
}

Тест, который не прошел с ошибкой «Отсутствует карта от пациента к модели PatientViewModel. Создайте с помощью Mapper.CreateMap '.

   [Fact]
    public async void InfohosServiceReturnsPatientViewModels()
    {
        var db = _container.Resolve<IInfohosDb>();

        var search = new PaginatedSearchBase();
        search.OrderBy = "Naam";

        var mapper = _container.Resolve<IMapper>();

        var result = await search.PagedResultAsAsync<Patient,PatientViewModel >(null,db.Patienten,mapper);
    }

PaginatedSearchBase

public class PaginatedSearchBase
{
    public string OrderBy { get; set; }
    public bool OrderDescending { get; set; }
    public int Page { get; set; } = 1;
    public int PageSize { get; set; } = 10;
}

И, наконец, расширение, вызывающее ProjectTo

public static class PagedResultExtensions
{
    public static async Task<PagedResult<T>> PagedResultAsync<T>(this PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context) where T : class
    {
        int totalCount;
        var query = PrepareQuery(vm, whereCollection, context, out totalCount);

        return new PagedResult<T>
        {
            Results = await query.ToListAsync(),
            Page = vm.Page,
            PageSize = vm.PageSize,
            Total = totalCount
        };
    }
    public static async Task<PagedResult<TAs>> PagedResultAsAsync<T, TAs>(this PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context, IMapper mapper) where T : class
    {
        int totalCount;
        var query = PrepareQuery(vm, whereCollection, context, out totalCount);

        return new PagedResult<TAs>
        {
----------> Results = await query.ProjectTo<TAs>(mapper).ToListAsync(),
            Page = vm.Page,
            PageSize = vm.PageSize,
            Total = totalCount
        };
    }

    private static IQueryable<T> PrepareQuery<T>(PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context,
        out int totalCount) where T : class
    {
        var query = context.AsQueryable();
        if (whereCollection != null)
        {
            foreach (var w in whereCollection)
            {
                if (w != null)
                {
                    query = query.Where(w);
                }
            }
        }
        // Order by
        query = query.OrderBy($"{vm.OrderBy} {(vm.OrderDescending ? "DESC" : "ASC")}");

        // Total rows
        totalCount = query.Count();

        // Paging
        query = query.Skip((vm.Page - 1)*vm.PageSize).Take(vm.PageSize);
        return query;
    }
}

Для информации использую версии:

  • «Автофак»: «4.0.0-RC1-177»
  • "Autofac.Extensions.DependencyInjection": "4.0.0-rc1-177"
  • «AutoMapper»: «4.2.1»

Изменить:

Новый тест, который я сделал, чтобы проверить, действительно ли сопоставления работают:

var mapper = _container.Resolve<IMapper>();
        var p = new Patient();
        p.Naam = "Test";
        var vm = mapper.Map<PatientViewModel>(p);

        vm.Naam.ShouldBeEquivalentTo("Test");

Этот тест проходит

Изменить 2:

Когда я вместо этого использую Map ‹> в Select (), он тоже работает, так что на самом деле ProjectTo‹> () терпит неудачу:

var results = await query.ToListAsync();
        return new PagedResult<TAs>
        {
            Results = results.Select(mapper.Map<TAs>).ToList(),
            Page = vm.Page,
            PageSize = vm.PageSize,
            Total = totalCount
        };

Это работает, но требует, чтобы сопоставитель был включен, а не внедрялся, и он не использует ProjectTo, который Automapper имеет для доступа к базе данных ...


person Appsum Solutions    schedule 19.05.2016    source источник
comment
Вы можете попробовать добавить using AutoMapper.QueryableExtensions;   -  person Sam Zakhezin    schedule 19.05.2016
comment
Я использую Visual Studio для кодирования, компилятор выдает ошибку, если ее нет в «использовании». Метод вызывается, но он не может получить экземпляр IMapper или что-то в этом роде, он не находит карту.   -  person Appsum Solutions    schedule 19.05.2016


Ответы (2)


Я столкнулся с той же проблемой, но мне удалось заставить ее работать. Это происходит из-за недавнего отказа Automapper от использования статических методов во всем API. Теперь, когда все основано на экземплярах, статические методы расширения больше не знают о конфигурации сопоставления, и теперь их нужно передать в метод. В итоге я зарегистрировал экземпляр MapperConfiguration как IConfigurationProvider (я использую Unity)

container.RegisterInstance(typeof (IConfigurationProvider), config);

Это вводится в мой обработчик запросов:

[Dependency]
public IConfigurationProvider MapperConfigurationProvider { get; set; }

Наконец, MapperConfigurationProvider передается вызову ProjectTo:

.ProjectTo<Payment>(MapperConfigurationProvider);

Надеюсь это поможет.

person Jeff    schedule 07.06.2016
comment
Спасибо, добавление MapperConfigurationProvider действительно решило проблему! Поскольку мой PagedResultAsAsync статичен, я добавляю IMapper в свой метод. Я предпочитаю вводить IMapper, потому что при необходимости могу использовать mapper.Map (). Я изменил свое утверждение на: query.ProjectTo ‹TAs› (mapper.ConfigurationProvider) .ToListAsync () - person Appsum Solutions; 08.06.2016

Вам не нужно специально добавлять ConfigurationProvider в DI. Если вы уже добавили IMapper в DI, вы можете прочитать ConfigurationProvider из самого Mapper. Пример: у меня была такая же проблема, и я создал базовый класс, содержащий IMapper, который был введен:

public abstract class ServiceBase { public IMapper Mapper { get; set; } }

Этот класс был унаследован от всех моих Сервисов, которые использовали AutoMapper. Теперь каждый раз, когда какой-либо из моих служб требовалось что-то сопоставить, они делали это:

    return context.SomeEntity
        .Where(e => e.Id == filter.Id)
        .ProjectTo<EntityDto>(Mapper.ConfigurationProvider).ToList();

При введении Mapper. Пока вы помещаете полностью настроенный Mapper в DI, все в порядке.

container.Register(Component.For<IMapper>().UsingFactoryMethod(x =>
    {
        return new AutoMapperConfig().ConfigureMapper();
    })
    );
person Cubelaster    schedule 07.11.2018