Как я могу использовать AutoMapper для свойств, помеченных как внутренние?

У меня есть решение с несколькими проектами. Проект бизнес-компонентов, веб-приложение MVC, проект DTO и ViewModels, проект модульного тестирования бизнес-компонентов и проект модульного тестирования MVC. В общем, не так уж и необычно. Бизнес-компонент имел ссылку на несколько конечных точек WCF. Внутри бизнес-компонента контракты данных из конечных точек WCF автоматически преобразуются с помощью AutoMapper в данные, необходимые для ViewModels. Проблема, которую я хотел решить, заключалась в том, что POCO контракта данных в автоматически сгенерированных прокси-серверах WCF являются PUBLIC, поэтому, когда я ссылаюсь на свой бизнес-компонент из своего веб-приложения MVC (фактически вводится через StructureMap, поэтому я могу использовать фиктивный бизнес-компонент, если мне нужно ), У меня есть доступ к WCF POCO из веб-приложения. Поскольку над веб-приложением будут работать несколько других разработчиков, я бы предпочел, чтобы они не поддавались соблазну напрямую использовать WCF POCO, а вместо этого использовали бизнес-компоненты. Поэтому я удалил ссылку на службу в бизнес-компонентах и ​​вместо этого добавил сценарий, который вызывает SVCUTIL с флагом / INTERNAL, чтобы автоматически сгенерированные классы были помечены как ВНУТРЕННИЕ, а не общедоступные. Однако теперь AutoMapper не будет сопоставляться с моим контрактом данных POCO.

Мне не удалось найти никакой документации, которая показала бы мне, как заставить AutoMapper работать с ВНУТРЕННИМИ свойствами, поэтому я вытащил источник из github и изменил TypeInfo.cs, чтобы он игнорировал поля и включал закрытые члены. Теперь мое решение работает отлично, но я чувствую себя довольно хакерским, имея собственную версию AutoMapper. Кажется, должен быть способ сопоставить POCO контракта данных WCF без необходимости быть PUBLIC. Что мне не хватает?

Изменен TypeInfo.cs

private IEnumerable<MemberInfo> GetAllPublicReadableMembers()
{
    IEnumerable<Type> typesToScan = new[] { Type, Type.BaseType };

    if (Type.IsInterface)
        typesToScan = typesToScan.Concat(Type.GetInterfaces());

    return typesToScan
        .Where(x => x != null)
        .SelectMany(x => x.FindMembers(
            MemberTypes.Property, //changed this
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, //and this
            (m, f) => m is FieldInfo ||
                      m is PropertyInfo && ((PropertyInfo)m).CanRead && !((PropertyInfo)m).GetIndexParameters().Any(),
            null)
        );
}

person Pat P    schedule 08.07.2010    source источник


Ответы (3)


Просто установите свойство ShouldMapProperty вашего объекта конфигурации в методе инициализации.

Вот пример использования статического API, однако вы сможете добиться того же аналогичным образом, используя нестатический API.

Mapper.Initialize(i =>
{
    i.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
    i.CreateMap<Source, Target>();                
});

Если вы используете профиль, это должно быть в конструкторе:

public class MyProfile : Profile
{
    public MyProfile()
    {
        ShouldMapProperty = arg => arg.GetMethod.IsPublic || arg.GetMethod.IsAssembly;

        // The mappings here.
    }
}
person CShark    schedule 23.05.2016
comment
Теперь это часть IMapperConfiguration. Пример: new MapperConfiguration(cfg => { cfg.ShouldMapProperty = ...; }); - person xr280xr; 08.11.2017

Вы думали вместо этого о сопоставлении с интерфейсами? Попросите контракт данных реализовать интерфейс, а затем просто сопоставить его. Затем вы можете явно реализовать интерфейс, эффективно скрывая эти члены.

person Jimmy Bogard    schedule 09.07.2010
comment
Да, похоже, это работает, но это означает, что мне понадобятся интерфейсы для соответствия десяткам контрактов данных, а затем редактирование автоматически сгенерированных контрактов данных для наследования интерфейса. Жаль, что SVCUtil не может создавать интерфейсы с клиентскими прокси. Сам писать интерфейсы - это больше, чем просто писать собственные картографы. :( - person Pat P; 09.07.2010
comment
Думаю, я могу просто включить этот патч обратно в складку. Нет никакой технической причины, по которой я не могу отобразить все свойства, независимо от того, являются они внутренними или нет. - person Jimmy Bogard; 09.07.2010
comment
Это своего рода хакерство, поскольку исключает возможность использовать поля вместо свойств. Мне поля не нужны, но некоторые могут. Может быть, было бы лучше иметь возможность внедрить пользовательский искатель свойств / методов / полей / и т. Д. В процесс настройки, во многом аналогичный тому, как работают пользовательские форматеры. В любом случае, продолжайте в том же духе. - person Pat P; 09.07.2010
comment
Кто-нибудь знает, попадало ли это когда-нибудь в базу кода AutoMapper? Я использую v1.1 и имею ту же проблему. Есть идеи, когда выйдет версия 1.2 или версия 2? - person SonOfPirate; 16.03.2011
comment
Привет, Джимми, это будет включено в следующий выпуск? Было бы неплохо иметь возможность использовать внутренние компоненты и по-прежнему иметь AutoMapper, устанавливаемый через NuGet. - person avesse; 25.08.2011

Использовать это. Он отлично работает с частными и внутренними полями.

http://ragingpenguin.com/code/privatefieldresolver.cs.txt

использование:

// in one library
public class Foo
{
internal string _bar = "some value";
}

// in another library
public class FooModel
{
public string Bar { get; set; }
}

Mapper.CreateMap<Foo, FooModel>()
.ForMember(x => x.Bar, o => o.ResolveUsing(new PrivateFieldResolver("_bar")));

Работает как шарм.

person Drew    schedule 03.11.2011
comment
кажется хорошим ответом, но без кода его +1 / -1 - person BozoJoe; 01.06.2013